"make"不仅对构建编程项目很有用,而且在其他领域似乎还没有得到充分利用.
例如,许多shell脚本可以重写为Makefile,以允许独立的部分并行运行(使用"make -jXX")以保持所有CPU内核的繁忙,并将显式声明的依赖项作为额外的好处,以防万一考虑在shell脚本中重新排序带副作用的一些任务.
你有任何有趣的故事与make/Makefiles的不同寻常的用途分享?您是否使用其他实用程序作为廉价的作业调度程序?
我有一个稍微hackish makefile用于运行测试:
### Run the tests
tests := tests/test1 tests/test2 ...
test: $(tests)
$(tests): %: %.c
gcc -o $@ $(testflags) $<
$@
Run Code Online (Sandbox Code Playgroud)
它有效,但它使Make做了我以前从未见过的事情.我的测试目前已被破坏,并导致总线错误.Make提供以下输出:
gcc -o tests/test1 [flags blah blah] tests/test1.c
tests/test1
make: *** [tests/test1] Bus error
make: *** Deleting file `tests/test1'
Run Code Online (Sandbox Code Playgroud)
我对最后一行感到好奇.我以前从未见过Make.为什么Make删除已编译的测试?
注意:我非常重视这个例子以使其更简单.我可能会介绍一些错误.
我怎么能告诉gnu不要并行构建一些配方.假设我有以下makefile:
sources = a.xxx b.xxx c.xxx
target = program
all : $(target)
$(target) : $(patsubst %.xxx,%.o,$(sources))
$(CXX) -o $@ $<
%.o : %.cpp
$(CXX) -c -o $@ $<
%.cpp : %.xxx
my-pre-processor -o $@ $<
Run Code Online (Sandbox Code Playgroud)
但是,该my-pre-processor命令创建具有固定名称的临时文件(我无法更改此名称).如果我只使用没有-j参数的make,这工作正常.但是,如果使用该-j选项,则构建有时会失败,因为两次并发调用会my-pre-processor覆盖其临时文件.
我想知道是否有办法告诉make它必须不构建并行化%.cpp : %.xxx配方执行的尝试.
GNU/Make手册§5.7声明如下:
5.7递归使用make
递归使用make意味着使用make作为makefile中的命令.当您需要为构成更大系统的各种子系统创建单独的makefile时,此技术非常有用.例如,假设您有一个子目录subdir,它有自己的makefile,并且您希望包含目录的makefile在子目录上运行make.你可以这样写:
Run Code Online (Sandbox Code Playgroud)subsystem: cd subdir && $(MAKE) or, equivalently, this (see Summary of Options): subsystem: $(MAKE) -C subdir
所以,基本上它意味着它cd subdir && $(MAKE)是一样的$(MAKE) -C subdir.
然而,事实证明它并不是真正的等价物.使用-C选项时,目录路径(如果是符号链接)始终是取消引用的,因此无法获得"逻辑"(如在pwd -L)目录名称.请考虑以下示例.Makefile在/tmp/b目录中具有以下内容,这是/tmp/a/目录的符号链接:
foo:
@echo PWDL=$(shell pwd -L)
@echo PWDP=$(shell pwd -P)
@echo CURDIR=$(CURDIR)
Run Code Online (Sandbox Code Playgroud)
现在,让我们以make不同的方式调用:
$ pwd
/tmp
$ (cd b && make foo)
PWDL=/tmp/b
PWDP=/tmp/a
CURDIR=/tmp/a
$ make -C b foo
make: Entering directory `/tmp/a'
PWDL=/tmp/a
PWDP=/tmp/a
CURDIR=/tmp/a
make: …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写一个make函数来触摸/创建一个空文件和/或在可能的情况下设置权限,用户和组,或者如果没有则发出警告.但是,我的函数中的每个条件检查似乎都评估为true.
我的Makefile的基本要素是
INSTALL_USER := fileUser
INSTALL_GROUP := fileGroup
.PHONY: test
test:
$(call touchFile,~/test.ini)
define touchFile
$(eval fileName := $(strip $(1)))
-touch $(fileName)
-chmod -c 664 $(fileName)
$(info filename info $(fileName))
$(info $(shell stat -c "%a %U:%G" $(fileName)))
$(if ifeq "foo" "bar", @echo match is broken, @echo match works)
$(if ifneq "foo" "bar", @echo match works, @echo match is broken)
$(if ifneq ($(shell stat -c %a $(fileName)),664), $(warning Error - $(fileName) does not have expected permissions of 664))
-chgrp -c $(INSTALL_GROUP) …Run Code Online (Sandbox Code Playgroud) 我有一个如下所示的makefile:
m1:
@echo building m1
m1_:
@echo building m1_
m2:
@echo building m2
m2_:
@echo building m2_
m3_DEPS = m2 m1
SUBSTITUTE=$(patsubst %,%_,$($@_DEPS))
.SECONDEXPANSION:
#%: $$(SUBSTITUTE)
%: $$(patsubst \%,\%_,$$($$@_DEPS))
@echo Building $@
@echo Dependencies are $^
Run Code Online (Sandbox Code Playgroud)
关键是
%: $$(patsubst \%,\%_,$$($$@_DEPS))
Run Code Online (Sandbox Code Playgroud)
我正在使用模式规则和patsubst,它本身使用百分号.我以为我可以%用a 逃脱角色\,但我仍然没有得到预期的行为.运行"make m3"给出输出
building m2
building m1
Building m3
Dependencies are m2 m1
Run Code Online (Sandbox Code Playgroud)
但是,我希望得到
building m2_
building m1_
Building m3
Dependencies are m2_ m1_
Run Code Online (Sandbox Code Playgroud)
注释掉该行并patsubst通过变量间接调用确实产生了该输出.
SUBSTITUTE=$(patsubst %,%_,$($@_DEPS))
%: $$(SUBSTITUTE)
Run Code Online (Sandbox Code Playgroud)
另外,我已经测试过使用非模式规则有效,这让我觉得它与模式规则和百分号的交互有关:
m3: $$(patsubst %,%_,$$($$@_DEPS))
Run Code Online (Sandbox Code Playgroud) foobar 即使失败也可以创建输出文件,所以在这种情况下我需要删除它.
我可以做这个:
foo: bar baz
foobar $^ -o $@ || (rm -f $@ && exit 1)
Run Code Online (Sandbox Code Playgroud)
但是这不会传播返回的相同退出代码foobar(然后由其输出make).有没有办法在Makefile中捕获错误而不是在shell中?
我可以从中得到一个值package.json:
LAST_VERSION := $(shell node -p "require('./package.json').version")
Run Code Online (Sandbox Code Playgroud)
但是,如果我需要几个值呢?喜欢:
PROJECT := $(shell node -p "require('./package.json').name")
LAST_VERSION:= $(shell node -p "require('./package.json').version")
DESCRIPTION := $(shell node -p "require('./package.json').description")
PROJECT_URL := $(shell node -p "require('./package.json').repository.url")
Run Code Online (Sandbox Code Playgroud)
这是唯一的方法吗?也许有办法创建一种列表.
我想用GNU make工具为我的微控制器构建一个C项目.我想以一种干净的方式做到这一点,这样我的源代码在构建之后就不会被目标文件和其他东西弄得乱七八糟.所以想象一下,我有一个名为"myProject"的项目文件夹,里面有两个文件夹:
- myProject
|
|---+ source
|
'---+ build
Run Code Online (Sandbox Code Playgroud)
build文件夹只包含一个makefile.下图显示了运行GNU make工具时应该发生的事情:
所以GNU make应该为它可以在源文件夹中找到的每个.c源文件创建一个目标文件.目标文件应该在与源文件夹中的结构类似的目录树中构建.
GNU make还应为每个.c源文件创建一个.d依赖文件(事实上,依赖文件是某种makefile本身).GNU make手册第4.14节"自动生成先决条件"中描述了依赖项文件:
对于每个源文件name.c,都有一个makefile name.d,它列出了目标文件name.o所依赖的文件.
从下面的问题#1 关于GNU使相关文件*.D,我了解到,增加的选项-MMD,并-MP给CFLAGS了GNU gcc编译器可以帮助自动执行.
所以现在问题就出现了.有没有人有一个样本makefile执行这样的源外构建?或者有关如何入门的一些好建议?
我很确定大多数编写过这样一个makefile的人都是Linux用户.但是微控制器项目也应该在Windows机器上构建.无论如何,即使你的makefile只是Linux,它提供了一个很好的起点;-)
PS:我想避免使用像CMake,Autotools或任何与IDE有关的任何工具.只是纯粹的GNU make.
我会很感激 :-)
更新依赖项文件
请看一下这个问题:当GNU更新.d文件时,确切的事件链是什么?