MASMのアセンブル・ビルドをバッチファイルで自動化しようの巻

プログラミング・技術系

どうも音又です。
以前より趣味でファミコンのゲームを自作するのにハマっており、開発でアセンブリ言語などを扱っていたのですが、普段PythonやJS/TSばかり使っている身としては慣れない記述が多く、雰囲気でしかアセンブリ言語を理解していませんでした。
そんなこんなで一度しっかり勉強してみようと思い、アセンブリ言語に関する書籍などを購入し、今勉強しているところです。

上記の書籍を読んでいますが、進める中でソースのアセンブルやビルドの際に毎回コマンドプロンプトでコマンドを叩くのは骨が折れる為、今回はそちらをバッチファイルで自動化してみることにします。

おそらくmakefileなどでも出来るかと思いますが、今回はわかりやすくバッチファイルを利用します。

スポンサーリンク

やることの流れ

まずビルドまでの流れとして手打ちの際のコマンドを見てみます。

:: asmソースをアセンブル
ml /c /coff hello.asm

:: アセンブルしたobjファイルをexeファイルにビルド
link /SUBSYSTEM:CONSOLE hello.obj

このようにmlコマンドでアセンブリソースをオブジェクトにアセンブルしてから、オブジェクトファイルを実行ファイルにビルドする流れとなります。

上記の用にml.exe実行ファイルなどを直接指定せず、mlコマンドのように使用するにはmasm32のbinフォルダを環境変数へ追加しておく必要があります。

これをそのままバッチファイルにして毎回ファイル名などを変えて使うのでも良いのですが、今回は学習中ということもあり、
各ソースごとにビルドしたりディレクトリを分けたりしたいのでバッチファイルにビルドしたいアセンブリソースを引数として指定し、そのソースファイルのあるディレクトリにbuildというフォルダを作ってその中にオブジェクトファイルやビルドした実行ファイルを入れるようにしてみます。

実際のバッチファイル

いろいろすっとばしますが、とりあえず実際にできたバッチファイルを見てみましょう。
今回はバッチファイルの名前はbuild.batとしています。

@echo off
setlocal

:: 01. 引数の確認
if "%~1"=="" (
    echo [ERROR] アセンブリファイルのパスを指定してください。
    exit /b 1
)

:: 02. 必要な変数の定義
set "SRC_DIR=%~dp1"
set "SRC_NAME=%~n1"
set "BUILD_DIR=%SRC_DIR%build"

:: 03. buildディレクトリの作成・既存ファイルの削除
if exist "%BUILD_DIR%" (
    echo [INFO] build ディレクトリが既に存在します。中身を削除します。
    del /q "%BUILD_DIR%\*" >nul 2>&1
) else (
    echo [INFO] build ディレクトリを作成します。
    mkdir "%BUILD_DIR%"
)

:: 04. カレントディレクトリの移動
pushd "%BUILD_DIR%"

:: 05. asmソースをアセンブル
echo [INFO] OBJファイルを生成中...(%BUILD_DIR%\%SRC_NAME%.obj)
ml /c /coff "%SRC_DIR%%SRC_NAME%.asm"
if errorlevel 1 (
    echo [ERROR] mlによるビルドに失敗しました。
    exit /b 1
)

:: 06. objファイルからexeファイルを生成
echo [INFO] EXEファイルを生成中...(%BUILD_DIR%\%SRC_NAME%.exe)
link /SUBSYSTEM:CONSOLE "%SRC_NAME%.obj" /OUT:"%SRC_NAME%.exe"
if errorlevel 1 (
    echo [ERROR] linkによるリンクに失敗しました。
    exit /b 1
)

echo [SUCCESS] ビルド完了: %BUILD_DIR%\%SRC_NAME%.exe

endlocal

では頭から見ていきましょう。

@echo off、setlocal...endlocal

まず@echo offはバッチファイル実行時の表示を見やすくするもので、バッチファイル内で使用されたコマンドの実行結果だけを表示してスッキリした見た目にします。
バッチファイルを書く時は大体最初に書くおまじないのようなものと思っておけば良いです。

次にsetlocal...endlocalですが、こちらは実際の処理の最初と最後にそれぞれ記述しており、これに囲まれた中で定義された環境変数をここ限りのものとして使用できるようにします。
環境変数の定義はこの後に解説しますが、本来環境変数を定義してしまうとPC内に定義が残ってしまい、余計な環境変数が増えてしまいますが、このsetlocal...endlocalで囲ってあげることでこの中で定義された環境変数を処理終了後に自動で破棄してくれるので予測出来ない環境変数のエラーなどを防ぐことができます。

01. 引数の確認

ここではバッチファイルに渡された引数が存在するかどうかを確認しています。
%~1というのが引数を表しておりこちらが=""、つまり空白の場合はエラーを出して処理を終了します。
ここをアレンジして、例えば引数が空白の場合はバッチファイルの存在するディレクトリ内からアセンブリファイルを探し出して処理対象とするようにするなどの応用も効かせられるかと思います。

02. 必要な変数の定義

先ほどのsetlocal...endlocalの説明の際に話した環境変数の定義がこちらになります。

set "SRC_DIR=%~dp1"は引数で指定されたアセンブリソースのディレクトリのパス部分を抽出して、ソースファイルのあるディレクトリを代入しています。
後でbuildフォルダを作成する際などに使用します。

set "SRC_NAME=%~n1"は指定されたアセンブリソースのファイル名部分を抽出して、ソースファイルの名前として代入しています。
こちらで定義されたものを生成されたオブジェクトファイルや実行ファイルの名前としても使用していきます。

set "BUILD_DIR=%SRC_DIR%build"buildフォルダのディレクトリを定義しています。
ビルドされたファイルなどの出力先指定などで使用します。

03. buildディレクトリの作成・既存ファイルの削除

ここでは指定されたアセンブリソースのあるディレクトリにbuildディレクトリを新たに作成、もしくは既にある場合は既存のオブジェクトファイルや実行ファイルを削除してお掃除します。
ビルド時に古いオブジェクトファイルなどが混ざっていると上手くビルド出来ない場合があるので、予め削除するようにしています。

04. カレントディレクトリの移動

ソースのアセンブルやビルドを行う前にカレントディレクトリ(作業する場所)を移動してあげて、ちゃんとbuildフォルダ内にファイルが生成されるようにします。

05. asmソースをアセンブル

実際にアセンブリソースをアセンブルする処理です。
ソースファイルの指定に環境変数を使用しているだけで、ほとんど先程紹介したコマンドと変わりません。
エラーが発生した場合は処理を中断します。

06. objファイルからexeファイルを生成

こちらは05で生成されたオブジェクトファイルをビルドして実行ファイル(.exe)を生成する処理です。
こちらも環境変数を使用した記述になっているだけでほとんど先程紹介したコマンドと変わりませんね。
ただし、ここでは生成した実行ファイルの出力先ディレクトリを/OUT: <ディレクトリ>のように指定することで任意のディレクトリで保存できるようになっています。

実際に実行してみる

ではこのバッチファイルを実際に実行してみましょう。
バッチファイルのあるディレクトリでコマンドプロンプトを開き、以下のようにコマンドを入力します。

build.bat chapter2\hello.asm

ここではchapter2フォルダにあるhello.asmをソースファイルとして指定しています。

バッチファイルのディレクトリでコマンドプロンプトを開くには通常通りcdコマンドなどでカレントディレクトリを移動しても良いですが、
エクスプローラーのアドレスバーにcmdと入力することでエクスプローラーで開いているフォルダをカレントディレクトリとしたコマンドプロンプトのプロセスが開きます。
設定すればWindows Terminalなどでも開けるようになるため覚えておくと損はないかと思います。

成功すれば以下のようなログが表示されて、chapter2フォルダ内にbuildフォルダとその中にオブジェクトファイルや実行ファイルが生成されます。

[INFO] build ディレクトリを作成します。
[INFO] OBJファイルを生成中...(C:\myproject\asm-practice\chapter2\build\hello.obj)
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: C:\myproject\asm-practice\chapter2\hello.asm

***********
ASCII build
***********

[INFO] EXEファイルを生成中...(C:\myproject\asm-practice\chapter2\build\hello.exe)
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

[SUCCESS] ビルド完了: C:\myproject\asm-practice\chapter2\build\hello.exe

buildフォルダが既にある場合は最初のログ部分が以下のように変わります。

[INFO] build ディレクトリが既に存在します。中身を削除します。

引数指定がされていない場合にエラーが出るかも確認してみましょう。

C:\myproject\asm-practice>build.bat                   
[ERROR] アセンブリファイルのパスを指定してください。

問題ありませんね!

これでアセンブルやビルドのコマンドを毎回入力する手間が省けるようになりました。
もちろん現在の記述だと例えばasmファイルでなかった場合などもそのまま処理を実行しようとしてしまう為、拡張子の確認処理を追加したり、複数のソースファイルをまとめて処理できるように拡張しても良いかと思います。

おわり

今回は以上になります。
開発中のCLIでの操作は毎回同じようなコマンドを入力させられがちなので、このようにバッチファイルやシェルスクリプトを組むことで開発効率も向上し、タイポによるエラーなども防げるので積極的に利用することをオススメします。
では今回はこのへんで。

コメント

タイトルとURLをコピーしました