產生來源檔案

有時候,來源檔案在傳遞給實際編譯器之前需要先進行預處理。舉例來說,您可能想要建置一個 IDL 編譯器,然後執行一些檔案來產生實際的來源檔案。在 Meson 中,這是透過 generator()custom_target() 來完成的。

使用 custom_target()

假設您有一個建置目標必須使用編譯器產生的來源檔案來建置。編譯器可以是已建置的目標

mycomp = executable('mycompiler', 'compiler.c')

或是由系統提供的外部程式,或來源樹內的腳本

mycomp = find_program('mycompiler')

自訂目標可以接受零個或多個輸入檔案,並使用它們產生一個或多個輸出檔案。透過使用自訂目標,您可以在建置時執行此編譯器來產生來源檔案

gen_src = custom_target('gen-output',
                        input : ['somefile1.c', 'file2.c'],
                        output : ['out.c', 'out.h'],
                        command : [mycomp, '@INPUT@',
                                   '--c-out', '@OUTPUT0@',
                                   '--h-out', '@OUTPUT1@'])

那裡的 @INPUT@ 會被轉換為 'somefile1.c' 'file2.c'。就像輸出一樣,您也可以透過索引個別參照每個輸入檔案。

然後您只需將其放入您的程式中,就完成了。

產生標頭檔

將產生的標頭檔新增到來源清單中,將確保產生標頭檔,並為目標建立正確的包含路徑

prog_python = find_program('python3')

foo_c = custom_target(
    'foo.c',
    output : 'foo.c',
    input : 'my_gen.py',
    command : [prog_python, '@INPUT@', '--code', '@OUTPUT@'],
)

foo_h = custom_target(
    'foo.h',
    output : 'foo.h',
    input : 'my_gen.py',
    command : [prog_python, '@INPUT@', '--header', '@OUTPUT@'],
)

libfoo = static_library('foo', [foo_c, foo_h])

executable('myexe', ['main.c', foo_h], link_with : libfoo)

每個依賴產生標頭檔的目標都應該將該標頭檔新增到其來源中,如上面 libfoomyexe 所見。這是因為 Meson 或後端無法知道 myexe 依賴 foo.h,僅僅因為 libfoo 依賴它,它可能是一個私有的標頭檔。

一次產生多個檔案

有時,單一產生器一次建立兩個或多個檔案(也許是一個標頭檔和來源檔)是有意義的,Meson 也涵蓋了這種情況。可以像列表一樣索引 custom_target,以分別取得每個輸出檔案。順序與 custom_target 的輸出引數順序相同

prog_python = find_program('python3')

foo_ch = custom_target(
    'foo.[ch]',
    output : ['foo.c', 'foo.h'],
    input : 'my_gen.py',
    command : [prog_python, '@INPUT@', '@OUTPUT@'],
)

libfoo = static_library('foo', [foo_ch])

executable('myexe', ['main.c', foo_ch[1]], link_with : libfoo)

在這種情況下,libfoo 依賴 foo.cfoo.h,但 myexe 僅依賴 foo.h,即第二個輸出。

使用相依性來管理產生的資源

在某些情況下,使用 declare_dependency 來「捆綁」標頭檔和函式庫相依性可能會更容易,特別是當有很多產生的標頭檔時

idep_foo = declare_dependency(
    sources : [foo_h, bar_h],
    link_with : [libfoo],
)

如需更多資訊,請參閱相依性declare_dependency()

使用 generator()

產生器類似於自訂目標,只是我們定義一個產生器,它定義如何將輸入檔案轉換為一個或多個輸出檔案,然後在我們想要的任意多個輸入檔案上使用它。

請注意,產生器僅應在將僅用作建置目標或自訂目標輸入的輸出上使用。當您在多個目標中使用產生器的處理輸出時,產生器將執行多次,以為每個目標建立輸出。每個輸出都將在目標私有的 @BUILD_DIR@ 目錄中建立。

如果您想要產生用於一般用途的檔案,例如產生多個來源使用的標頭檔,或將要安裝的資料等等,請改用 custom_target()

gen = generator(mycomp,
                output  : '@BASENAME@.c',
                arguments : ['@INPUT@', '@OUTPUT@'])

第一個引數是要執行的執行檔。下一個檔案指定名稱產生規則。它指定如何為給定的輸入名稱建置輸出檔案名稱。@BASENAME@ 是輸入檔案名稱的佔位符,不帶前導路徑或後綴(如果有的話)。因此,如果輸入檔案名稱為 some/path/filename.idl,則輸出名稱將為 filename.c。您也可以使用 @PLAINNAME@,它會保留後綴,這將產生一個名為 filename.idl.c 的檔案。最後一行指定要傳遞給執行檔的命令列引數。@INPUT@@OUTPUT@ 分別是輸入和輸出檔案的佔位符,Meson 會自動填入。如果您的規則產生多個輸出檔案,並且您需要將它們傳遞給命令列,請將位置附加到輸出持有者,例如:@OUTPUT0@@OUTPUT1@ 等。

指定此規則後,我們可以產生來源檔案並將其新增到目標中。

gen_src = gen.process('input1.idl', 'input2.idl')
executable('program', 'main.c', gen_src)

產生器還可以產生具有未知名稱的多個輸出檔案

gen2 = generator(someprog,
                 output : ['@BASENAME@.c', '@BASENAME@.h'],
                 arguments : ['--out_dir=@BUILD_DIR@', '@INPUT@'])

在這種情況下,您不能使用普通的 @OUTPUT@ 變數,因為它會造成歧義。此程式只需要知道輸出目錄,它會自行產生檔案名稱。

為了使每次使用時都可以將不同的額外引數傳遞給產生器程式,您可以在 arguments 清單中使用 @EXTRA_ARGS@ 字串。請注意,此佔位符只能作為一個完整的字串存在,而不能作為子字串。主要原因是它代表一個字串列表,該列表可能為空,或包含多個元素;並且在任何一種情況下,將其插入到單個字串的中間都是有問題的。如果沒有從 process() 呼叫中傳遞任何額外引數,則會從實際引數清單中完全省略該佔位符,因此不會因此將空字串傳遞給產生器程式。如果 extra_args 中有多個元素,則它們會作為單獨的元素插入到實際引數清單中。

gen3 = generator(genprog,
                 output : '@BASENAME@.cc',
                 arguments : ['@INPUT@', '@EXTRA_ARGS@', '@OUTPUT@'])
gen3_src1 = gen3.process('input1.y')
gen3_src2 = gen3.process('input2.y', extra_args: '--foo')
gen3_src3 = gen3.process('input3.y', extra_args: ['--foo', '--bar'])

搜尋結果是