xmakeは、Luaをベースにした軽量で最新のc / c ++プロジェクトビルドツールです。主な機能は、シンプルな構文と使いやすさ、より読みやすいプロジェクトメンテナンスの提供、クロスプラットフォームの動作に一貫したビルドエクスペリエンスの実現です。
この記事では主に、カスタムスクリプトを追加して、スクリプトドメインでより複雑で柔軟なカスタマイズを実現する方法について詳しく説明します。
構成の分離
xmake.luaは、28の原則を採用して、記述ドメインとスクリプトドメインの2層分離構成を実現します。
何それは単に、プロジェクトの設定、例の80%のほとんどは、より多くの従来の構成をベースとしている28件の原則は、次のような、以下のとおりですadd_cxflags
、add_links
ので、上の
残りの場所の唯一の20%未満のみ追加コンプレックスを行う必要がありますいくつかの特別な構成要件を満たすため。
構成の残りの20%は通常、より複雑です.xmake.lua全体に直接フラッディングすると、プロジェクト全体の構成が乱雑になり、非常に読みにくくなります。
したがって、xmakeは、記述ドメインとスクリプトドメインの2つの異なる構成方法によって、単純な構成の80%と複雑な構成の20%を分離し、xmake.lua全体を非常に明確で直感的で、読みやすく、保守しやすいものにします。
説明フィールド
始めたばかりの初心者ユーザー、または単純な小さなプロジェクトを維持しているだけのユーザーの場合、構成を完全に説明することで要件を完全に満たすことができます。説明ドメインとは何ですか。次のようになります。
target("test")
set_kind("binary")
add_files("src/*.c")
add_defines("DEBUG")
add_syslinks("pthread")
一見すると、実際にはset_xxx
/のadd_xxx
構成セットです。初心者の場合、いくつかの基本的なルールを持つ通常の構成ファイルのように、luaスクリプトとして扱う必要はありません。
これは構成ファイルのように見えますか?実際、記述ドメインは、jsonなどのキー/値の構成に似た構成ファイルであるため、Luaをまったく知らない初心者でもすぐに始めることができます。
また、通常のプロジェクトでは、set_xxx/add_xxx
さまざまなプロジェクト設定を構成するだけで、要件を完全に満たすことができます。
これは私が最初に言ったことです。ケースの80%で、最も単純な構成ルールを使用してプロジェクトの構成を簡素化し、読みやすさと保守性を向上させることができるため、ユーザーと開発者にとって非常に使いやすく直感的です。
さまざまなプラットフォームやアーキテクチャに対して条件付きの判断を下したい場合はどうなりますか?基本的な構成に加えて、説明フィールドは条件付き判断とforループもサポートします。
target("test")
set_kind("binary")
add_files("src/*.c")
add_defines("DEBUG")
if is_plat("linux", "macosx") then
add_links("pthread", "m", "dl")
end
target("test")
set_kind("binary")
add_files("src/*.c")
add_defines("DEBUG")
for _, name in ipairs({"pthread", "m", "dl"}) do
add_links(name)
end
これはLuaに少し似ていますか?通常、これは一般的な構成の問題と見なすことができますが、xmakeは結局luaに基づいているため、記述ドメインは引き続きluaの基本的な言語機能をサポートします。
!>ただし、descriptionフィールドはLuaのスクリプト構文をサポートしていますが、時間のかかる関数呼び出しやforループなど、説明フィールドに複雑すぎるLuaスクリプトを記述しないようにしてください。
また、記述ドメインでは、主な目的は構成項目を設定することです。したがって、xmakeはすべてのモジュールインターフェースを完全に開くわけではありません。多くのインターフェースは記述ドメインで呼び出すことを禁じられています。
一部の開いている呼び出し可能なインターフェースでさえ完全に読み取り専用です。時間-次のような安全インターフェースの消費:os.getenv()
構成ロジック制御のために従来のシステム情報の読み取りを待機しています。
!>もう1つの注意点は、xmake.luaが複数回解析されて、さまざまな段階でさまざまな構成ドメイン(:など)option()
やtarget()
その他のドメインが解決されることです。
したがって、xmake.luaの説明フィールドに複雑なluaスクリプトを書くことを考えないでください。また、説明フィールドでprintを呼び出して情報を表示しないでください。複数回実行されるため、覚えておいてください。複数回実行されます。!!
スクリプトドメイン
記述ドメインを制限して複雑なluaを記述しますが、すべての種類のluaモジュールとインターフェースを使用することはできませんか?実行する方法?これは、スクリプトドメインが表示される時間です。
ユーザーがxmakeの説明フィールドの構成に既に完全に精通していて、プロジェクトの特別な構成の保守には不十分であると感じた場合は、スクリプトフィールドでより複雑な構成ロジックを実行できます。
target("test")
set_kind("binary")
add_files("src/*.c")
on_load(function (target)
if is_plat("linux", "macosx") then
target:add("links", "pthread", "m", "dl")
end
end)
after_build(function (target)
import("core.project.config")
local targetfile = target:targetfile()
os.cp(targetfile, path.join(config.buildir(), path.filename(targetfile)))
print("build %s", targetfile)
end)
:長期に類似のようにon_xxx
、after_xxx
、before_xxx
ドメインに属しているスクリプトの本体と言い換える内部機能、スクリプト。
スクリプトドメインでは、ユーザーは何でもできます。xmakeは、xmakeに組み込まれているさまざまなluaモジュールをインポートするためのインポートインターフェイスを提供し、ユーザーが提供するluaスクリプトをインポートすることもできます。
スクリプトドメインに必要な関数を実装したり、別のプロジェクトを作成したりすることもできます。
一部のスクリプトフラグメントでは、肥大化していない場合は、上記のような組み込みを記述するだけで十分です。より複雑なスクリプトを実装する必要があり、xmake.luaに入力したくない場合は、メンテナンスのためにスクリプトを別のluaファイルに入れます。
例えば:
target("test")
set_kind("binary")
add_files("src/*.c")
on_load("modules.test.load")
on_install("modules.test.install")
カスタマイズしたスクリプトをxmake.luaの対応するディレクトリに配置modules/test/load.lua
し、個別にmodules/test/install.lua
管理できます。
単一のluaスクリプトファイルは、メインエントランスとしてmainを使用します。次に例を示します。
-- 我们也可以在此处导入一些内置模块或者自己的扩展模块来使用
import("core.project.config")
import("mymodule")
function main(target)
if is_plat("linux", "macosx") then
target:add("links", "pthread", "m", "dl")
end
end
内部にあるこれらの独立したluaスクリプトは、さまざまな組み込みモジュールをインポートしてインポートすることもでき、カスタムモジュールが使用されます。通常はluaと書くだけで、Javaは区別されません。
スクリプトドメインのさまざまな段階で、on_load
主にターゲットの読み込み時に動的な構成を行うために使用されます。説明ドメインとは異なり、実行されるのは1回だけです!!!
他にも、次のような多くの段階がありますon/after/before
。_build/install/package/run
など。以下で詳しく説明します。
インポート
拡張モジュールのインポート
各スクリプトドメインについて説明する前に、xmakeのモジュールのインポートと使用方法を簡単に紹介しましょう。xmakeはimportを使用して、他の拡張モジュールとユーザー定義モジュールをインポートします。次の場所で使用できます。
インポートメカニズムは次のとおりです。
- 最初に現在のスクリプトディレクトリからインポートします
- 拡張ライブラリからインポート
インポートされた文法規則:
.
クラスライブラリのパスルールに基づいて、次に例を示します。
import("core.base.option")
import("core.base.task")
function main()
-- 获取参数选项
print(option.get("version"))
-- 运行任务和插件
task.run("hello")
end
現在のディレクトリにカスタムモジュールをインポートします。
ディレクトリ構造:
plugin
- xmake.lua
- main.lua
- modules
- hello1.lua
- hello2.lua
main.luaにモジュールをインポートします
import("modules.hello1")
import("modules.hello2")
インポート後、内部のすべてのパブリックインターフェイスを直接使用できます。プライベートインターフェイスには_
、外部からエクスポートまたは呼び出されないことを示すプレフィックスが付いています。。
現在のディレクトリに加えて、他の指定されたディレクトリにクラスライブラリをインポートすることもできます。
import("hello3", {rootdir = "/home/xxx/modules"})
名前の競合を防ぐために、インポート後にエイリアスを指定することもできます。
import("core.platform.platform", {alias = "p"})
function main()
-- 这样我们就可以使用p来调用platform模块的plats接口,获取所有xmake支持的平台列表了
print(p.plats())
end
バージョン2.1.5では、2つの新しい属性が追加されています。import("xxx.xxx", {try = true, anonymous = true})
tryがtrueの場合、インポートされたモジュールが存在しないと、nilのみが返され、例外をスローした後にxmakeが中断されません。anonymous
がtrueの場合、インポートされたモジュールは現在のスコープにインポートされず、インポートされたモジュールのみがインポートされます。オブジェクト参照はインポートインターフェイスに返されます。
拡張モジュールをテストします
on_loadなどのスクリプトでprintを直接呼び出して、テストと検証のためにモジュールの呼び出し結果情報を出力する1つの方法。
ただし、xmakeは、xmake lua
より柔軟で便利なプラグインテストスクリプトも提供します。
指定されたスクリプトファイルを実行します
たとえば、ロードして実行するLuaスクリプトを直接指定できます。これは、一部のインターフェイスモジュールをすばやくテストし、アイデアの一部を検証するための優れた方法です。
最初に簡単なluaスクリプトを書いてみましょう:
function main()
print("hello xmake!")
end
次に、直接実行します。
$ xmake lua /tmp/test.lua
拡張モジュールを直接呼び出す
次に、すべての組み込みモジュールと拡張モジュールのインターフェースをxmake lua
直接呼び出すことができます。
$ xmake lua lib.detect.find_tool gcc
上記のコマンドでは、import("lib.detect.find_tool")
モジュールインターフェイスを直接呼び出して、すばやく実行します。
対話型コマンド(REPL)の実行
インタラクティブモードでは、コマンドの実行が一部のモジュールとAPIのテストと検証に便利であり、柔軟性も高い場合があります。ロードするために追加のスクリプトファイルを作成する必要はありません。
まず、インタラクティブモードに入る方法を見てみましょう。
# 不带任何参数执行,就可以进入
$ xmake lua
>
# 进行表达式计算
> 1 + 2
3
# 赋值和打印变量值
> a = 1
> a
1
# 多行输入和执行
> for _, v in pairs({1, 2, 3}) do
>> print(v)
>> end
1
2
3
import
拡張モジュールをインポートすることもできます。
> task = import("core.project.task")
> task.run("hello")
hello xmake!
あなたは複数行の入力をキャンセルしたい場合は途中で、あなただけの文字入力する必要があります。q
それはそれです
> for _, v in ipairs({1, 2}) do
>> print(v)
>> q <-- 取消多行输入,清空先前的输入数据
> 1 + 2
3
target:on_load
カスタムターゲットロードスクリプト
このスクリプトは、ターゲットが初期化されてロードされるときに実行され、動的なターゲット構成を実行して、より柔軟なターゲット記述定義を実現できます。次に例を示します。
target("test")
on_load(function (target)
target:add("defines", "DEBUG", "TEST=\"hello\"")
target:add("linkdirs", "/usr/lib", "/usr/local/lib")
target:add({includedirs = "/usr/include", "links" = "pthread"})
end)
することができon_load
内部を通ってtarget:set
、target:add
動的に様々なターゲットプロパティを追加するには、説明フィールドのすべてがset_
、add_
設定が動的にこのように構成することができます。
さらに、ターゲットのいくつかのインターフェースを呼び出して、次のようないくつかの基本情報を取得および設定できます。
ターゲットインターフェイス | 説明 |
---|---|
target:name() | ターゲット名を取得する |
target:targetfile() | ターゲットファイルのパスを取得する |
target:targetkind() | ターゲットのビルドタイプを取得します |
target:get( "defines") | ターゲットのマクロ定義を取得します |
target:get(“ xxx”) | その他のset_/add_ ターゲット情報インターフェース設定により、このインターフェースから取得できます |
target:add( "links"、 "pthread") | 目標設定を追加する |
target:set( "links"、 "pthread"、 "z") | 目標設定を上書きする |
target:deps() | ターゲットのすべての依存ターゲットを取得します |
target:dep(“ depname”) | 指定された依存関係ターゲットを取得します |
target:opts() | ターゲットに関連するすべてのオプションを取得します |
target:opt(“ optname”) | 指定された関連付けオプションを取得します |
target:pkgs() | ターゲットの関連するすべての依存関係パッケージを取得します |
target:pkg(“ pkgname”) | 指定された関連する依存関係パッケージを取得します |
target:sourcebatches() | ターゲットのすべてのソースファイルのリストを取得します |
target:on_link
カスタムリンクスクリプト
これは、v2.2.7の後に追加された新しいインターフェイスであり、ターゲットのリンクプロセスをカスタマイズするために使用されます。
target("test")
on_link(function (target)
print("link it")
end)
target:on_build
カスタムコンパイルスクリプト
ターゲットターゲットのデフォルトのビルド動作をオーバーライドし、カスタムコンパイルプロセスを実装します。通常、xmakeがデフォルトで提供しないコンパイル操作を実際に実行する必要がない限り、これを実行する必要はありません。
次の方法でオーバーライドして、コンパイル操作をカスタマイズできます。
target("test")
-- 设置自定义编译脚本
on_build(function (target)
print("build it")
end)
注:バージョン2.1.5以降、すべてのターゲットカスタムスクリプトは、プラットフォームやアーキテクチャごとに個別に処理できます。次に例を示します。
target("test")
on_build("iphoneos|arm*", function (target)
print("build for iphoneos and arm")
end)
その中で、最初のパラメータが文字列の場合、平台|架构
実行する前に実行する必要のあるスクリプトを指定することであり、arm*
すべてのARMアーキテクチャのマッチングなどのパターンマッチングをサポートします。
もちろん、アーキテクチャを設定せずにプラットフォームを設定することもできます。これは、指定されたプラットフォームと一致し、スクリプトを実行するためです。
target("test")
on_build("windows", function (target)
print("build for windows")
end)
注:このターゲットに独自のビルドプロセスを設定すると、xmakeのデフォルトのビルドプロセスは実行されなくなります。
target:on_build_file
コンパイルスクリプトをカスタマイズして、単一ファイルの構築を実現します
このインターフェイスを介して、指定されたターゲットの組み込みビルドプロセスをフックし、各ソースファイルのコンパイルプロセスを自分で再実装するために使用できます。
target("test")
set_kind("binary")
add_files("src/*.c")
on_build_file(function (target, sourcefile, opt)
end)
target:on_build_files
コンパイルスクリプトをカスタマイズして、複数ファイルの構築を実現します
このインターフェイスを介して、指定されたターゲットの組み込みビルドプロセスをフックし、同じタイプのソースファイルのバッチのコンパイルプロセスを置き換えるために使用できます。
target("test")
set_kind("binary")
add_files("src/*.c")
on_build_files(function (target, sourcebatch, opt)
end)
このインターフェースを設定した後、これは包含関係であるため、対応するソースファイルリスト内のファイルは、カスタマイズされたtarget.on_build_fileに表示されません。
sourcebatchは、同じタイプのソースファイルのこのバッチを記述します。
sourcebatch.sourcekind
:次のようなソースファイルのこのバッチのタイプを取得します:cc、as、…sourcebatch.sourcefiles()
:ソースファイルのリストを取得するsourcebatch.objectfiles()
:ターゲットファイルのリストを取得しますsourcebatch.dependfiles()
:対応する依存ファイルのリストを取得し、コンパイル依存情報をソースファイルに保存します(例:xxx.d)。
target:on_clean
カスタムクリーンアップスクリプト
ターゲットのxmake [c|clean}
クリーンアップ操作をオーバーライドして、カスタムクリーンアッププロセスを実装します。
target("test")
-- 设置自定义清理脚本
on_clean(function (target)
-- 仅删掉目标文件
os.rm(target:targetfile())
end)
target:on_package
カスタムパッケージスクリプト
ターゲットターゲットxmake [p|package}
のパッケージ化操作をオーバーライドし、カスタムパッケージングプロセスを実装します。指定したターゲットを必要な形式にパッケージ化する場合は、このインターフェイスを使用してカスタマイズできます。
target("demo")
set_kind("shared")
add_files("jni/*.c")
on_package(function (target)
os.exec("./gradlew app:assembleDebug")
end)
もちろん、この例は少し古いです。これは使用法を説明するための単なる例です。xmakeは、gradleとより適切に統合するための特別なxmake-gradleプラグインを提供します。
target:on_install
カスタムインストールスクリプト
ターゲットターゲットのxmake [i|install}
インストール操作をオーバーライドして、カスタムインストールプロセスを実装します。
たとえば、生成されたapkパッケージをインストールします。
target("test")
-- 设置自定义安装脚本,自动安装apk文件
on_install(function (target)
-- 使用adb安装打包生成的apk文件
os.run("adb install -r ./bin/Demo-debug.apk")
end)
target:on_uninstall
カスタムアンインストールスクリプト
ターゲットのxmake [u|uninstall}
アンインストール操作をオーバーライドして、カスタムアンインストールプロセスを実装します。
target("test")
on_uninstall(function (target)
...
end)
target:on_run
カスタム実行スクリプト
ターゲットのxmake [r|run}
実行中の操作をオーバーライドして、カスタムの実行中のプロセスを実現します。
たとえば、インストールされているapkプログラムを実行します。
target("test")
-- 设置自定义运行脚本,自动运行安装好的app程序,并且自动获取设备输出信息
on_run(function (target)
os.run("adb shell am start -n com.demo/com.demo.DemoTest")
os.run("adb logcat")
end)
before_xxx和after_xxx
target:on_xxxのすべてのインターフェースが内部のデフォルト実装をカバーしていることに注意してください。通常、完全に書き直す必要はありません。独自のロジックを追加するだけでtarget:before_xxx
、target:after_xxx
シリーズスクリプトを使用できます。
すべてのon_xxxには対応するbefore_バージョンとafter_xxバージョンがあり、パラメーターはまったく同じです。次に例を示します。
target("test")
before_build(function (target)
print("")
end)
内蔵モジュール
カスタムスクリプトでは、インポートインターフェイスを使用してさまざまな拡張モジュールをインポートして使用することに加えて、xmakeは、os、ioなどの基本操作など、より多くのクロスを実現するための多くの基本的な組み込みモジュールも提供します。 -プラットフォーム処理システムインターフェイス。
os.cp
os.cpの動作はシェルのcp
コマンドに似ていますが、より強力です。パターンマッチング(luaパターンマッチングを使用)をサポートするだけでなく、宛先パスの再帰的なディレクトリ作成を保証し、組み込みのxmakeの変数で。
例えば:
os.cp("$(scriptdir)/*.h", "$(buildir)/inc")
os.cp("$(projectdir)/src/test/**.h", "$(buildir)/inc")
上記のコードはxmake.lua
、現在のディレクトリ内のすべてのヘッダーファイルと、プロジェクトソースコードのテストディレクトリ内のヘッダーファイルを$(buildir)
出力ディレクトリにコピーします。
その中でも$(scriptdir)
、$(projectdir)
これらの変数は、内蔵されているxmakeの変数。詳細については、を参照してくださいの関連文書組み込み変数。
一方*.h
、**.h
一致するパターンは、前者と同様のadd_filesを使用して、単一ステージの一致ディレクトリであり、これはマルチレベルのディレクトリ再帰一致です。
上記のコピーでは、すべてのファイルが展開され、指定されたディレクトリにコピーされ、ソースディレクトリレベルが失われます。元のディレクトリ構造に従ってコピーする場合は、rootdirパラメータを設定できます。
os.cp("src/**.h", "/tmp/", {rootdir = "src"})
上記のスクリプトは、src
ルートディレクトリに従ってディレクトリ構造を維持しながら、srcの下にあるすべてのサブファイルをコピーできます。
注:プラットフォームの一貫性を確保し、クロスプラットフォームの構造記述を実現するためos.cp
にos.run("cp ..")
、代わりにインターフェースを使用するようにしてください。
os.run
このインターフェイスは、サードパーティのシェルコマンドを実行するためのネイティブシェルコマンドを静かに実行しますが、出力をエコーせず、エラーが発生した後にのみエラーメッセージを強調表示します。
このインターフェースは、パラメーターのフォーマットと、次のような組み込み変数をサポートします。
-- 格式化参数传入
os.run("echo hello %s!", "xmake")
-- 列举构建目录文件
os.run("ls -l $(buildir)")
os.execv
os.runと比較すると、このインターフェイスは実行中に出力をエコーし、パラメーターはより柔軟なリストを介して渡されます。
os.execv("echo", {"hello", "xmake!"})
さらに、このインターフェースは、設定を渡すためのオプションのパラメーターもサポートします。出力のリダイレクト、環境変数設定の実行などです。
os.execv("echo", {"hello", "xmake!"}, {stdout = outfile, stderr = errfile, envs = {PATH = "xxx;xx", CFLAGS = "xx", curdir = "/tmp"}}
その中で、stdoutパラメーターとstderrパラメーターは、リダイレクトされた出力とエラー出力を渡すために使用されます。これらは、io.openによって開かれたファイルパスまたはファイルオブジェクトに直接渡すことができます。
さらに、この実行中に一部の環境変数を一時的に設定および書き換える場合は、envsパラメーターを渡すことができます。内部の環境変数設定は既存の設定を置き換えますが、外部の実行環境には影響せず、現在のコマンドのみに影響します。
また、os.getenvs()
インターフェイスを介して現在のすべての環境変数を取得し、一部を書き直した後にenvsパラメーターを渡すこともできます。
さらに、curdirパラメータ設定により、実行中に子プロセスの作業ディレクトリを変更できます。
関連する同様のインターフェイスには、os.runv、os.exec、os.execv、os.iorun、os.iorunvなどがあります。たとえば、os.iorunは操作の出力コンテンツを取得できます。
この領域の具体的な詳細と違い、およびその他のosインターフェイスについては、osインターフェイスのドキュメントを参照してください。
io.readfile
このインターフェイスは、指定されたパスファイルからすべてのコンテンツを読み取ります。ファイルを開かなくても、ファイル全体のコンテンツを直接読み取ることができます。これは、次のように便利です。
local data = io.readfile("xxx.txt")
io.writefile
このインターフェイスは、指定されたパスのファイルにすべてのコンテンツを書き込みます。ファイルを開かなくても、ファイル全体のコンテンツを直接書き込むことができます。これは、次のように便利です。
io.writefile("xxx.txt", "all data")
path.join
このインターフェイスは、クロスプラットフォームのパススプライシング操作を実装し、複数のパスアイテムを追加およびスプライシングします。windows/unix
スタイルのパスの違いにより、次のようにapiを使用してパスを追加する方がクロスプラットフォームです。
print(path.join("$(tmpdir)", "dir1", "dir2", "file.txt"))
上記のスプライシングは、UNIXの場合と同等であり$(tmpdir)/dir1/dir2/file.txt
、Windowsの場合と同等です。$(tmpdir)\\dir1\\dir2\\file.txt
組み込みモジュールの詳細については、以下を参照してください。組み込みモジュールのドキュメント