根据 git 描述输出重建目标
Rebuild make targets based upon git describe output
我正在尝试在我的二进制文件中集成 git 描述的输出:
// version.c.in
#include <stdio.h>
const char version[] __attribute__((used)) = "##BUILDVERSION##:@BUILDVERSION@";
int main() {
printf("%s\n", version);
return 0;
}
Make 从中生成一个 version.c:
// Makefile
SOURCEFILES = $(wildcard *.c) version.c
PROGRAMS = $(addprefix build/,$(SOURCEFILES:.c=.out))
.PHONY: version.c
all: $(PROGRAMS)
clean:
rm -fR build
build/%.out: build/%.o Makefile
mkdir -p build
gcc -g $< -o $@
build/%.o: %.c Makefile
mkdir -p build
gcc -Wall -Werror -g -c $< -o $@
version.c: version.c.in Makefile
cp $< $@_temp
sed -i "s/@BUILDVERSION@/$$(git describe --always --dirty=-dirty)/g" $@_temp
@cmp -s $@_temp $@ || mv $@_temp $@
rm -f $@_temp
我想实现仅当 git 的输出描述发生变化时才触发重建。使用我目前的方法,链接器步骤总是被调用,尽管 version.c 甚至没有改变。但它是 .PHONY,这可能会导致依赖于它的所有目标的重建。如果我不再将目标设置为 .PHONY,那么我将错过重建,例如,如果我提交或添加标签。
我也无法在 .git/ 中找到合适的文件作为 version.c 的可能依赖项。
有办法解决吗?
编辑:另见 to 这是我最喜欢的新解决方案。我会把这个留在这里作为记录。
But it is .PHONY, which probably causes the rebuild of all targets depending on it.
这是正确的:.PHONY
目标总是被重建,这意味着依赖于它的任何东西也 总是 通常被重建(参见 )。
由于 make
的文件和时间戳方向,您需要的是一个命令,当且仅当文件内容发生变化时更新某个文件的时间戳。然后您可以让输出 O 取决于输入文件 I,其时间戳取决于 git describe
输出。
这是一个完整的例子:
version=$(shell git describe --always --dirty=-dirty)
$(shell echo ${version} > version.tmp && \
{ cmp -s version.tmp version.txt || cp version.tmp version.txt; } && \
rm -f version.tmp)
all: version.o
version.c: version.c.in version.txt
sed "s:@BUILDVERSION@:${version}:" < $@.in > $@
version.o: version.c
即使在旧版本的 gmake 中也应该可以工作;较新的具有 $(file)
可用于完成大部分工作的功能。也就是说,您将使用 $(file)
读取 version.txt
的内容,然后只有当这些内容与 git describe
输出的内容不匹配时,才写入 version.txt
,全部完成在读取 Makefile 期间(就像这里的情况:注意 $(shell)
调用是如何在早期发生的)。
(编辑以添加注释:all
确实应该在此处标记为 .PHONY
,因为
。我显然没有打扰。是否将所有输出隔离到 build/
由您决定。)
您将目标文件存储在 build
目录中,因此生成的 version.c
也应该放在该目录中。
您可以将当前提交哈希存储到 build
目录中的临时文件中,并使 build/version.c
依赖它。
目标 all
和 clean
必须标记为 .PHONY
。
Makefile
中的评论从 #
开始。
build/version.c 每次 cause build/version.o 被移除时都会重建。为避免这种删除,可以使用 Makefile removes object files for no reason
中的解决方案
所以,通常可能的解决方案可能是这样的:
# Makefile
SOURCEFILES = $(wildcard *.c.in)
OBJS = $(addprefix build/,$(SOURCEFILES:.c.in=.o))
PROGRAMS = $(addprefix build/,$(SOURCEFILES:.c.in=.out))
.PHONY: all
all: $(PROGRAMS)
.PHONY: clean
clean:
rm -fR build
build/%.out: build/%.o Makefile
mkdir -p build
gcc -g $< -o $@
build/%.o: %.c build/%.c Makefile
mkdir -p build
gcc -Wall -Werror -g -c $< -o $@
# .SECONDARY: build/version.o
.PRECIOUS: build/version.o
build/current_hash:
mkdir -p build
git log --pretty=%h > $@.tmp
@cmp -s $@.tmp $@ || mv $@.tmp $@
build/version.c: version.c.in Makefile build/current_hash
cp $< $@_temp
sed -i "s/@BUILDVERSION@/$$(git describe --always --dirty=-dirty)/g" $@_temp
@cmp -s $@_temp $@ || mv $@_temp $@
rm -f $@_temp
.PHONY: force
version.c: force
version=`git describe --always --dirty || echo unknown:non-git`; \
grep -qsF \"$$version\" version.c \
|| printf >version.c 'const char version[]="%s";\n' $$version
和 version.c 配方将 运行 用于任何依赖版本字符串的构建,version.o
仅当版本号实际更改时才重建。
然后将 extern const char version[];
放入 header 中,您将获得绝对最小的重建传播。
我正在尝试在我的二进制文件中集成 git 描述的输出:
// version.c.in
#include <stdio.h>
const char version[] __attribute__((used)) = "##BUILDVERSION##:@BUILDVERSION@";
int main() {
printf("%s\n", version);
return 0;
}
Make 从中生成一个 version.c:
// Makefile
SOURCEFILES = $(wildcard *.c) version.c
PROGRAMS = $(addprefix build/,$(SOURCEFILES:.c=.out))
.PHONY: version.c
all: $(PROGRAMS)
clean:
rm -fR build
build/%.out: build/%.o Makefile
mkdir -p build
gcc -g $< -o $@
build/%.o: %.c Makefile
mkdir -p build
gcc -Wall -Werror -g -c $< -o $@
version.c: version.c.in Makefile
cp $< $@_temp
sed -i "s/@BUILDVERSION@/$$(git describe --always --dirty=-dirty)/g" $@_temp
@cmp -s $@_temp $@ || mv $@_temp $@
rm -f $@_temp
我想实现仅当 git 的输出描述发生变化时才触发重建。使用我目前的方法,链接器步骤总是被调用,尽管 version.c 甚至没有改变。但它是 .PHONY,这可能会导致依赖于它的所有目标的重建。如果我不再将目标设置为 .PHONY,那么我将错过重建,例如,如果我提交或添加标签。
我也无法在 .git/ 中找到合适的文件作为 version.c 的可能依赖项。
有办法解决吗?
编辑:另见
But it is .PHONY, which probably causes the rebuild of all targets depending on it.
这是正确的:.PHONY
目标总是被重建,这意味着依赖于它的任何东西也 总是 通常被重建(参见
由于 make
的文件和时间戳方向,您需要的是一个命令,当且仅当文件内容发生变化时更新某个文件的时间戳。然后您可以让输出 O 取决于输入文件 I,其时间戳取决于 git describe
输出。
这是一个完整的例子:
version=$(shell git describe --always --dirty=-dirty)
$(shell echo ${version} > version.tmp && \
{ cmp -s version.tmp version.txt || cp version.tmp version.txt; } && \
rm -f version.tmp)
all: version.o
version.c: version.c.in version.txt
sed "s:@BUILDVERSION@:${version}:" < $@.in > $@
version.o: version.c
即使在旧版本的 gmake 中也应该可以工作;较新的具有 $(file)
可用于完成大部分工作的功能。也就是说,您将使用 $(file)
读取 version.txt
的内容,然后只有当这些内容与 git describe
输出的内容不匹配时,才写入 version.txt
,全部完成在读取 Makefile 期间(就像这里的情况:注意 $(shell)
调用是如何在早期发生的)。
(编辑以添加注释:all
确实应该在此处标记为 .PHONY
,因为
build/
由您决定。)
您将目标文件存储在
build
目录中,因此生成的version.c
也应该放在该目录中。您可以将当前提交哈希存储到
build
目录中的临时文件中,并使build/version.c
依赖它。目标
all
和clean
必须标记为.PHONY
。Makefile
中的评论从#
开始。build/version.c 每次 cause build/version.o 被移除时都会重建。为避免这种删除,可以使用 Makefile removes object files for no reason
中的解决方案
所以,通常可能的解决方案可能是这样的:
# Makefile
SOURCEFILES = $(wildcard *.c.in)
OBJS = $(addprefix build/,$(SOURCEFILES:.c.in=.o))
PROGRAMS = $(addprefix build/,$(SOURCEFILES:.c.in=.out))
.PHONY: all
all: $(PROGRAMS)
.PHONY: clean
clean:
rm -fR build
build/%.out: build/%.o Makefile
mkdir -p build
gcc -g $< -o $@
build/%.o: %.c build/%.c Makefile
mkdir -p build
gcc -Wall -Werror -g -c $< -o $@
# .SECONDARY: build/version.o
.PRECIOUS: build/version.o
build/current_hash:
mkdir -p build
git log --pretty=%h > $@.tmp
@cmp -s $@.tmp $@ || mv $@.tmp $@
build/version.c: version.c.in Makefile build/current_hash
cp $< $@_temp
sed -i "s/@BUILDVERSION@/$$(git describe --always --dirty=-dirty)/g" $@_temp
@cmp -s $@_temp $@ || mv $@_temp $@
rm -f $@_temp
.PHONY: force
version.c: force
version=`git describe --always --dirty || echo unknown:non-git`; \
grep -qsF \"$$version\" version.c \
|| printf >version.c 'const char version[]="%s";\n' $$version
和 version.c 配方将 运行 用于任何依赖版本字符串的构建,version.o
仅当版本号实际更改时才重建。
然后将 extern const char version[];
放入 header 中,您将获得绝对最小的重建传播。