将变量从 Makefile 传递到可执行文件的最佳方式是什么?
Which is the best way to pass a variable from a Makefile to an executable?
我想在编译时在 Makefile 中生成一个变量,并在 运行 时在我的可执行文件中使用它。
最小示例:在 Makefile 中我生成变量 COMMIT_ID
,其中包含最新提交的 ID:COMMIT_ID=$(git rev-parse --verify HEAD)
.
Makefile 还生成一个可执行文件,例如 executable.elf
。
生成 executable.elf
的 C
源代码输出 COMMIT_ID
.
最好的处理方法是什么?
这取决于您的 makefile 的外观。如果您有访问它的 c 文件的专用规则,您可以将 -DCOMMIT_ID=$(COMMIT_ID)
添加到配方中,如下所示:
foo.o: foo.c
$(CC) $(CFLAGS) -DCOMMIT_ID=$(COMMIT_ID) $^ -o $@
或者,如果您正在使用模式规则,您可以将它添加到 CFLAGS,然后每个 .c 文件都可以访问它(当然假设模式规则使用 CFLAGS
):
CFLAGS += -DCOMMIT_ID=$(COMMIT_ID)
完成此操作后,COMMIT_ID 将成为 c 文件中的一个宏,因此您可以使用字符串化运算符 #
来访问其值:
printf("commit id is " #COMMIT_ID "\n");
----- 编辑 -----
请注意,这确实涉及到一个尖锐的棍子——具体来说,如果您构建一次,然后在不修改 foo.c 的情况下修改 git 存储库,那么 make 将考虑 foo.o 是最新的,并且不会重建它,这意味着 foo.o 将包含旧的提交 ID。我会 post 第二个答案,如果你担心这个问题,你会如何解决这个问题
您当然可以使用 make
变量,如下所示:
printf("%s\n", COMMIT_ID);
在那里你会有一个 Makefile
类似
executable.elf: CFLAGS+=-D'COMMIT_ID="$(shell git rev-parse --verify HEAD)"'
(这使用了一些不可移植的 GNU make
特性。)
但是,如果这实际上是可重现的,可能会更好。为此,也许将值写入文件,以便您可以看到上次编译该项目时使用的内容。
executable.elf: commitid.c
.PHONY: commitid.c
commitid.c:
git rev-parse --verify HEAD \
| sed 's/.*/#define COMMIT_ID "&"/' >$@
然后显然 #include "commitid.c"
来自 executable.c
或任何调用的 C 源文件。
强制commitid.c
每次都重建的.PHONY
声明有点生硬;也许改为 commitid.c
依赖于每个版本控制的文件。
如果 git 存储库发生变化,我的第一个答案是不会重建 executable.elf
,但您的源代码不会。如果您需要在存储库更改时始终重建它,您可以使用一些 makefile 技巧来做到这一点:(可能有更好的方法来做到这一点,如果有人能想到一个,我很想听听) :
首先在您的 makefile 中执行:
$(shell \
COMMIT_ID=$(git rev-parse --verify HEAD) && \
echo "#define COMMIT_ID $${COMMIT_ID}" > commitid.h.tmp; \
if [ ! -f commitid.h ] || ! cmp -s commitid.h.tmp commitid.h; then \
cp commitid.h.tmp commitid.h; \
fi; \
rm commitid.h.tmp \
)
然后在 foo.c
中,您只需执行
#include commitid.h
只要您的 makefile 生成 .d 文件,那么 foo.c 将依赖于 commitid.h,并且如果此文件更新,将会重建。 (如果没有,请将 foo.c: commitid.h
行添加到您的 makefile 中)。文件本身只会在提交 ID 更改时更新,因此 foo.o 只会在必要时重建。
书呆子狙击手 HardcoreHenry 引诱我寻求更好(或至少不同)的解决方案。想出了这个,在存储库中有一个文件,其名称表示上次构建存储库的提交:
COMMIT_ID := $(shell git rev-parse --verify HEAD)
# target is made if it does not exist
$(COMMIT_ID).commit_id:
@# clean other commits than current
rm -f *.commit_id
@# placeholder for current commit
touch $@
commit_id.h: $(COMMIT_ID).commit_id
echo "#define COMMIT_ID ${COMMIT_ID}" > $@
我想在编译时在 Makefile 中生成一个变量,并在 运行 时在我的可执行文件中使用它。
最小示例:在 Makefile 中我生成变量 COMMIT_ID
,其中包含最新提交的 ID:COMMIT_ID=$(git rev-parse --verify HEAD)
.
Makefile 还生成一个可执行文件,例如 executable.elf
。
生成 executable.elf
的 C
源代码输出 COMMIT_ID
.
最好的处理方法是什么?
这取决于您的 makefile 的外观。如果您有访问它的 c 文件的专用规则,您可以将 -DCOMMIT_ID=$(COMMIT_ID)
添加到配方中,如下所示:
foo.o: foo.c
$(CC) $(CFLAGS) -DCOMMIT_ID=$(COMMIT_ID) $^ -o $@
或者,如果您正在使用模式规则,您可以将它添加到 CFLAGS,然后每个 .c 文件都可以访问它(当然假设模式规则使用 CFLAGS
):
CFLAGS += -DCOMMIT_ID=$(COMMIT_ID)
完成此操作后,COMMIT_ID 将成为 c 文件中的一个宏,因此您可以使用字符串化运算符 #
来访问其值:
printf("commit id is " #COMMIT_ID "\n");
----- 编辑 -----
请注意,这确实涉及到一个尖锐的棍子——具体来说,如果您构建一次,然后在不修改 foo.c 的情况下修改 git 存储库,那么 make 将考虑 foo.o 是最新的,并且不会重建它,这意味着 foo.o 将包含旧的提交 ID。我会 post 第二个答案,如果你担心这个问题,你会如何解决这个问题
您当然可以使用 make
变量,如下所示:
printf("%s\n", COMMIT_ID);
在那里你会有一个 Makefile
类似
executable.elf: CFLAGS+=-D'COMMIT_ID="$(shell git rev-parse --verify HEAD)"'
(这使用了一些不可移植的 GNU make
特性。)
但是,如果这实际上是可重现的,可能会更好。为此,也许将值写入文件,以便您可以看到上次编译该项目时使用的内容。
executable.elf: commitid.c
.PHONY: commitid.c
commitid.c:
git rev-parse --verify HEAD \
| sed 's/.*/#define COMMIT_ID "&"/' >$@
然后显然 #include "commitid.c"
来自 executable.c
或任何调用的 C 源文件。
强制commitid.c
每次都重建的.PHONY
声明有点生硬;也许改为 commitid.c
依赖于每个版本控制的文件。
如果 git 存储库发生变化,我的第一个答案是不会重建 executable.elf
,但您的源代码不会。如果您需要在存储库更改时始终重建它,您可以使用一些 makefile 技巧来做到这一点:(可能有更好的方法来做到这一点,如果有人能想到一个,我很想听听) :
首先在您的 makefile 中执行:
$(shell \
COMMIT_ID=$(git rev-parse --verify HEAD) && \
echo "#define COMMIT_ID $${COMMIT_ID}" > commitid.h.tmp; \
if [ ! -f commitid.h ] || ! cmp -s commitid.h.tmp commitid.h; then \
cp commitid.h.tmp commitid.h; \
fi; \
rm commitid.h.tmp \
)
然后在 foo.c
中,您只需执行
#include commitid.h
只要您的 makefile 生成 .d 文件,那么 foo.c 将依赖于 commitid.h,并且如果此文件更新,将会重建。 (如果没有,请将 foo.c: commitid.h
行添加到您的 makefile 中)。文件本身只会在提交 ID 更改时更新,因此 foo.o 只会在必要时重建。
书呆子狙击手 HardcoreHenry 引诱我寻求更好(或至少不同)的解决方案。想出了这个,在存储库中有一个文件,其名称表示上次构建存储库的提交:
COMMIT_ID := $(shell git rev-parse --verify HEAD)
# target is made if it does not exist
$(COMMIT_ID).commit_id:
@# clean other commits than current
rm -f *.commit_id
@# placeholder for current commit
touch $@
commit_id.h: $(COMMIT_ID).commit_id
echo "#define COMMIT_ID ${COMMIT_ID}" > $@