在Shell命令中定位通配符 [英] Target wildcards in a shell command

查看:64
本文介绍了在Shell命令中定位通配符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建依赖于目录中目标名称的文件列表的目标:

I'm trying to create targets that depend on a list of files in a directory with the name of the target:

bin/%.out: src/%/ $(shell find src/%/* -type f -iname '*.cpp' -o -iname '*.hpp')
#   Build stuff here

但是shell find src/%/* ...最终扩展为shell find src//* ....起初我以为是因为我只能有1个目标通配符,但是即使除去了src/%/依赖关系,它仍然会遇到同样的问题.

However shell find src/%/* ...ends up expanding to shell find src//* .... At first I thought it's because I could only have 1 target wildcard but even after removing the src/%/dependency it ended up with this same problem.

更多上下文:我的目录包含一个"src"目录,其中包含目录.我将"src"的每个子目录视为一个项目".构建时,所有目标文件都应放在out/src/projname中.我试图使用find递归获取每个项目的所有源文件和头文件.所有二进制文件都将放入bin/projname.out中,因此,主要依赖项是bin中的.out文件,这些文件的名称与其项目名称相同. (如果存在src/abc,则bin/abc.outall的依赖项.)

Some more context: my directory contains a 'src' directory, which contains directories. Each sub-directory of 'src' I treat as a "project". When I build, all object files should go in out/src/projname. I'm attempting to use find to recursively get all source and header files per project. All binaries will go in bin/projname.out and as such, the main dependencies are .out files in bin that have the same names as their project name. (if src/abc exists, bin/abc.out is a dependency of all).

因此,对于src中的每个子目录,所有源文件都会被编译,目标文件将移至out/projname,最终将链接到bin/projname.out.

So for each sub-directory in src, all source files are compiled and the object files are moved to out/projname which eventually will be linked to bin/projname.out.

  • src
    • proj0
      • cpp/hpp文件
      • src
        • proj0
          • cpp/hpp files
          • cpp/hpp文件
          • src
            • proj0
              • 目标文件
              • src
                • proj0
                  • object files
                  • 目标文件
                  • proj0.out
                  • proj1.out

                  当前获得的项目列表和输出文件列表如下:

                  Currently getting the list of projects and list of output files as follows:

                  SRCS := $(shell find src/* -maxdepth 0 -type d)
                  SRCS_OUT := $(patsubst src/%,bin/%.out,$(SRCS))
                  ...
                  all: out/ bin/ $(SRCS_OUT)
                  

                  项目可以包含子目录,这就是为什么我在最上方的代码中使用find的原因.

                  Projects can contain sub-directories and such, which is why I'm using find in the code at the very top.

                  推荐答案

                  我看不到如何使用标准的,可移植的制造功能来完成所需的工作.但这可以通过高级GNU make功能来实现:

                  I do not see how to do what you want with standard, portable, make features. But it is doable with advanced GNU make features:

                  define PROJECT_rule
                  CPPS_$(1) := $$(shell find src/$(1) -type f -iname '*.cpp')
                  HPPS_$(1) := $$(shell find src/$(1) -type f -iname '*.hpp')
                  OBJS_$(1) := $$(patsubst src/$(1)/%.cpp,out/src/$(1)/%.o,$$(CPPS_$(1)))
                  
                  $$(OBJS_$(1)): out/src/$(1)/%.o: src/$(1)/%.cpp $$(HPPS_$(1))
                      <compile recipe>
                  
                  bin/$(1).out: $$(OBJS_$(1))
                      <link recipe>
                  endef
                  
                  PRJS := $(patsubst src/%,%,$(shell find src/* -maxdepth 0 -type d))
                  
                  $(foreach p,$(PRJS),$(eval $(call PROJECT_rule,$(p))))
                  

                  我们首先定义一个make变量(PROJECT_rule),它将用作您任何项目的模板;在此模板中,$(1)将代表您的项目的名称.然后,我们计算项目列表(PRJS),最后迭代项目并处理每个项目的模板(foreach-eval-call).

                  We first define a make variable (PROJECT_rule) that will be used as a template for any of your projects; in this template, $(1) will represent the name of your project. Then, we compute the list of projects (PRJS) and, finally, we iterate over the projects and process our template for each of them (foreach-eval-call).

                  注意:

                  1. 我假设一个项目的每个目标文件都依赖于同一项目的所有头文件.如果不是这种情况,您将不得不重新做一些工作.

                  1. I assumed that each object file of a project depends on all header files of the same project. If it is not the case you will have to rework this a bit.

                  要理解的重要一点是:

                  • PROJECT_rule变量首先由call处理,以用项目名称替换$(1).
                  • 然后通过eval对其进行递归和完全扩展(包括配方).递归表示如果我们有:

                  • The PROJECT_rule variable is first processed by call to substitute $(1) with the project's name.
                  • Then it is expanded recursively and completely (including the recipes) by eval. Recursively means that if we have:

                  FOO = foo
                  BAR = FOO
                  BAZ = $($(BAR))
                  QUX = $$(BAZ)
                  

                  $($(BAR))扩展为foo,而$$(BAZ)扩展为$(BAZ).完全意味着PROJET_rule变量的所有部分都被扩展,甚至最终被用作配方的内容(当make解析Makefile时,它通常将配方的扩展推迟到以后).

                  $($(BAR)) is expanded as foo while $$(BAZ) is expanded as $(BAZ). Completely means that all parts of the PROJET_rule variable are expanded, even what is finally used as recipes (while when make parses a Makefile, it normally defers the expansion of the recipes to a later stage).

                  结果将被实例化为常规make构造,也就是说,将在评估之前将其再次扩展为常规make构造(而不是配方).

                  The result will be instantiated as regular make constructs, that is, it will be expanded once more as regular make constructs (not the recipes), before being evaluated.

                  因此,由于PROJECT_rule将有两个扩展,因此必须转义某些$符号($$).编写自己的<compile recipe><link recipe>时,请记住这一点.例如,如果要使用$@$^自动变量,请不要忘记编写$$@.示例:如果您的编译配方为:

                  So, as there will be two expansions of PROJECT_rule, some $ signs must be escaped ($$). Please remember this when writing your own <compile recipe> and <link recipe>. If you want to use the $@ or $^ automatic variables, for instance, do not forget to write $$@. Example: if your compile recipe is:

                  $(CPP) $(CPPFLAGS) $(INCLUDES) -c $< -o $@
                  

                  写:

                  $$(CPP) $$(CPPFLAGS) $$(INCLUDES) -c $$< -o $$@
                  

                  PROJECT_rule定义中的

                  .第一次扩展会将其转换为:

                  in the definition of PROJECT_rule. The first expansion will transform it in:

                  $(CPP) $(CPPFLAGS) $(INCLUDES) -c $< -o $@
                  

                  同时,如果您写:

                  $(CPP) $(CPPFLAGS) $(INCLUDES) -c $< -o $@
                  

                  第一个扩展会将其转换为以下形式:

                  the first expansion will transform it in something like:

                  g++ -O3 -Iincludes/foobar -c  -o
                  

                  请注意,除非CPPCPPFLAGSINCLUDES具有特定于目标的特定定义,否则您还可以编写:

                  Note that, unless CPP, CPPFLAGS or INCLUDES have specific target-dependent definitions, you can also write:

                  $(CPP) $(CPPFLAGS) $(INCLUDES) -c $$< -o $$@
                  

                  因为第一个扩展会将其转换为:

                  because the first expansion will transform it in something like:

                  g++ -O3 -Iincludes/foobar -c $< -o $@
                  

                  这也是正确的.

                  更详细的解释:对于每个项目(例如proj0),foreach都会产生:

                  A bit more detailed explanation: for each project (e.g. proj0), foreach will produce:

                  $(eval $(call PROJECT_rule,proj0))
                  

                  call将在PROJECT_rule定义中将$(1)替换为proj0,这样eval的参数将为:

                  call will substitute $(1) with proj0 in the PROJECT_rule definition, such that the parameter of eval will be:

                  CPPS_proj0 := $$(shell find src/proj0 -type f -iname '*.cpp')
                  HPPS_proj0 := $$(shell find src/proj0 -type f -iname '*.hpp')
                  OBJS_proj0 := $$(patsubst src/proj0/%.cpp,out/src/proj0/%.o,$$(CPPS_proj0))
                  
                  $$(OBJS_proj0): out/src/proj0/%.o: src/proj0/%.cpp $$(HPPS_proj0)
                      <compile recipe>
                  
                  bin/proj0.out: $$(OBJS_proj0)
                      <link recipe>
                  

                  eval将对其进行递归和完全扩展,从而导致:

                  eval will expand it recursively and completely, leading to:

                  CPPS_proj0 := $(shell find src/proj0 -type f -iname '*.cpp')
                  HPPS_proj0 := $(shell find src/proj0 -type f -iname '*.hpp')
                  OBJS_proj0 := $(patsubst src/proj0/%.cpp,out/src/proj0/%.o,$(CPPS_proj0))
                  
                  $(OBJS_proj0): out/src/proj0/%.o: src/proj0/%.cpp $(HPPS_proj0)
                      <expanded compile recipe>
                  
                  bin/proj0.out: $(OBJS_proj0)
                      <expanded link recipe>
                  

                  (每个$$变为$即使在配方中).然后,eval将实例化为常规make构造,并将它们解析为常规make语法.也就是说,(简单)变量将立即扩展,目标和前提条件也将立即扩展,但配方不会扩展.食谱将在第二阶段扩展,就在传递到外壳并执行之前.

                  (each $$ becomes $, even in the recipes). eval will then instantiate the result as regular make constructs and they will be parsed as regular make syntax. That is, the (simple) variables will be expanded immediately, the targets and prerequisites too, but not the recipes. The recipes will be expanded in a second phase, just before being passed to the shell and executed, if they are.

                  编译规则是静态模式规则.如果您还不知道这一点,则可以阅读 GNU make文档的这一部分.

                  The compile rule is a static pattern rule. If you do not know this already you can read this section of the GNU make documentation.

                  锻炼:在PROJECT_rule的定义中,可以将$$(shell...替换为$(shell...,而不能将$$(patsubst...替换为$(patsubst....为什么?

                  Exercise: in the definition of PROJECT_rule you could replace $$(shell... by $(shell... but not $$(patsubst... by $(patsubst.... Why?

                  这篇关于在Shell命令中定位通配符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆