【问题标题】:Is there a non-transitive version of target-specific variables?是否有目标特定变量的非传递版本?
【发布时间】:2018-08-07 20:21:36
【问题描述】:

一段时间以来,我一直在使用特定于目标的变量来传递特定于对象的标志。例如,如果我需要将可执行文件与某个库链接,我可能会这样做:

bin/foo: LDFLAGS += -lbar

bin/% 规则将选择-lbar。这对系统库很有用,但是当我链接的库也是项目的一部分时,就会出现问题。在这种情况下,我会有一个类似的依赖项

bin/foo: lib/libbar.so

在构建lib/libbar.so 时,我应用于bin/foo 的特定于目标的规则也通过了!这不起作用,因为libbar.so 无法链接到自身。正如manual 所说,

目标特定变量还有一个特殊功能:当您定义目标特定变量时,该变量值也对该目标的所有先决条件及其所有先决条件等有效(除非这些先决条件覆盖具有自己的目标特定变量值的变量)。

我该怎么做才能只为bin/foo 添加该标志而不是它的先决条件?上面的引用暗示了类似

lib/libbar.so: LDFLAGS :=

但这并不理想,因为我的真实项目在全球范围内设置了其他 LDFLAGS 我想保留。

这是一个复制器:

生成文件

bin/%:
  @mkdir -p $(@D)
  gcc $(LDFLAGS) $(filter %.o,$^) -o $@

lib/%:
  @mkdir -p $(@D)
  gcc $(LDFLAGS) -shared $(filter %.o,$^) -o $@

obj/%.o: src/%.c
  @mkdir -p $(@D)
  gcc $(CFLAGS) -c $< -o $@

bin/foo: obj/foo.o lib/libbar.so
bin/foo: LDFLAGS := -Wl,-rpath='$$ORIGIN/../lib' -Llib -lbar

lib/libbar.so: obj/bar.o

clean:
  rm -rf bin lib obj

.PHONY: clean

src/foo.c

int bar(void);

int main() {
  return bar();
}

src/bar.c

int bar() {
  return 42;
}

这个坏了:

$ make bin/foo
gcc  -c src/foo.c -o obj/foo.o
gcc  -c src/bar.c -o obj/bar.o
gcc -Wl,-rpath='$ORIGIN/../lib' -Llib -lbar -shared obj/bar.o -o lib/libbar.so
/bin/ld: lib/libbar.so: file not recognized: file truncated
collect2: error: ld returned 1 exit status
make: *** [Makefile:7: lib/libbar.so] Error 1

但如果我先构建库,它就可以工作:

$ make lib/libbar.so
gcc  -c src/bar.c -o obj/bar.o
gcc  -shared obj/bar.o -o lib/libbar.so
$ make bin/foo
gcc  -c src/foo.c -o obj/foo.o
gcc -Wl,-rpath='$ORIGIN/../lib' -Llib -lbar obj/foo.o -o bin/foo
$ ./bin/foo; echo $?
42

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    您可以使用构造变量名称而不是特定于目标的变量。这是一种不同的语法,但允许特定的目标设置。

    类似:

    LDFLAGS += $($@_FLAGS)
    
    bin/foo_FLAGS = -lbar
    

    更多详情可以查看here (and related articles)

    简短的回答是没有可用的特定于目标的无继承功能。如果您不想继承,则必须使用不同的东西。

    【讨论】:

      【解决方案2】:

      我认为问题是 Unix 中(老式的,呃,抱歉)编译和链接方法的遗产。或者也许是我不知道某些上下文。我发现很难理解为什么库名称在链接器调用中被破坏以隐藏一个事实,即实际上我们链接的是一个接口,而不是一个实际的文件——尽管我们需要那个文件来提取对所述接口的微不足道的描述。那好吧。不管真正的原因是什么,make 对这种隐藏和扭曲的依赖信息不满意,因此您遇到了问题,因为对于make,您提供的信息既不是鱼也不是肉。也许是一个类似函数的变量,比如

      LIBSWITCHES = $(patsubst lib%,-l%,$(basename $(notdir $(filter %.so,$^))))
      

      可能会有所帮助,因为它试图重建依赖关系与链接库的关系。它从依赖列表中获取所有共享对象,并从中创建一个-l+库名称开关。由于您的库本身没有依赖项,因此它不会出现在此列表中,因此不应混淆链接器。只需取出所有 -l 开关,您实际上是从 $(LDFLAGS) 重建的名称库:

      bin/%:
          @mkdir -p $(@D)
          gcc $(LDFLAGS) $(LIBSWITCHES) $(filter %.o,$^) -o $@
      
      lib/%:
          @mkdir -p $(@D)
          gcc $(LDFLAGS) $(LIBSWITCHES) -shared $(filter %.o,$^) -o $@
      
      bin/foo: LDFLAGS := -Wl,-rpath='$$ORIGIN/../lib' -Llib
      

      【讨论】:

      • 谢谢!尽管我接受了 MadScientist 的回答,因为它回答了一般性问题,但我最终提出了与此建议类似的建议。
      • @TavianBarnes 如果您的解决方案有一些巧妙的转折,那么访问者会很乐意阅读它们!
      • 没什么聪明的,有点像$(foreach so,$(filter %.so,$^),-L$(dir $(so)) -l$(patsubst lib%.so,%,$(notdir $(so))))
      猜你喜欢
      • 2011-03-18
      • 2016-03-30
      • 1970-01-01
      • 2010-11-03
      • 2011-05-24
      • 1970-01-01
      • 2014-09-13
      • 2011-07-18
      • 1970-01-01
      相关资源
      最近更新 更多