【问题标题】:Iterating the values of a submake迭代子制作的值
【发布时间】:2021-11-12 12:21:39
【问题描述】:

我正在尝试自动化我在 Makefile 中经常使用的一些命令,但我似乎无法找出正确的语法。

鉴于以下目标:

.PHONY: params-list
params-list:
    @aws ssm get-parameters-by-path --path /${SERVICE}/${ENV} | jq -c -r '.Parameters[] | .Name'

.PHONY: params-get
params-get:
    @aws ssm get-parameter --name ${PARAM} --with-decryption | jq -c -r .Parameter.Value

我试图调用params-list,然后将结果输入params-get。我最好的尝试是:

.PHONY: params
params:
    for param in $(MAKE) params-list; do \
        $(MAKE) params-get PARAM=$${param}; \
    done

但显然行不通。实现这一目标的正确方法是什么?

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    一个提示是避免使用 make 的 shell 结构。一步一步来。

    对于 get 操作,我们为每个参数编码一个目标。所以对于参数p1(比如说),我们发明了一个目标params-get<p1>。我们注意到,在配方(即 shell 命令)内部,$@ 将扩展为 params-get<p1>。因此${@:params-get<%>=%} 将扩展为p1

    make 语法写出来:

    .PHONY: params-get<p1>
    params-get<p1>:
          @aws ssm get-parameter --name ${@:params-get<%>=%} --with-decryption | jq -c -r .Parameter.Value
    

    我注意到,使用中间变量,我们可以拥有与您在之前的咒语中使用的完全相同的配方。

    PARAM = ${@:params-get<%>=%}
    
    .PHONY: params-get<p1>
    params-get<p1>:
          @aws ssm get-parameter --name ${PARAM} --with-decryption | jq -c -r .Parameter.Value
    

    如果我们有第二个参数pa(比如说),那很容易添加:

    PARAM = ${@:params-get<%>=%}
    
    .PHONY: params-get<p1> params-get<pa>
    params-get<p1> params-get<pa>:
          @aws ssm get-parameter --name ${PARAM} …
    

    我希望你能在这里看到一些样板。

    转向一些更动态生成的make,而不是硬编码每个参数,让我们将所有可能的参数放在一个名为$PARAMS的列表中

    PARAMS := p1 pa another
    get-targets := ${PARAMS:%=params-get<%>}
    
    PARAM = ${@:params-get<%>=%}
    
    .PHONY: ${get-targets}
    ${get-targets}:
          @aws ssm get-parameter --name ${PARAM} …
    
    .PHONY: params-get
    params-get: ${get-targets}
          echo $@ Done 
    

    我已添加您原来的params-get 目标。现在所有工作都由依赖项完成。

    • 看不到外壳循环。
    • 每个aws ssm get-parameter 的退出代码由make 检查。
    • 代码是并行安全的:make -j9 将并行运行 9 个作业,直到工作耗尽。如果你有 8 个 CPU,那就太好了。 (如果您的 make 不是并行安全的,那么它就是 broken。)

    我们现在已经很接近了。我们只需确保将$PARAMS 设置为ssm get-parameters-by-path 的输出。

    类似:

    .PHONY: params-list
    params-list:
        @aws ssm get-parameters-by-path --path /${SERVICE}/${ENV} | jq -c -r '.Parameters[] | .Name'
    
    PARAMS := $(shell ${MAKE} params-list)
    get-targets := ${PARAMS:%=params-get<%>}
    
    PARAM = ${@:params-get<%>=%}
    
    .PHONY: ${get-targets}
    ${get-targets}:
          @aws ssm get-parameter --name ${PARAM} --with-decryption | jq -c -r .Parameter.Value
    
    .PHONY: params-get
    params-get: ${get-targets}
          echo $@ Done 
    

    绝对不喜欢使用$(shell …),但这只是一个草图。

    【讨论】:

      【解决方案2】:

      您可以按如下方式执行单个(长)目标/配方:

      .PHONY: params-get
      params-get:
          @for p in $$(aws ssm get-parameters-by-path --path /${SERVICE}/${ENV} | jq -c -r '.Parameters[] | .Name'); do \
              aws ssm get-parameter --name $$p --with-decryption | jq -c -r .Parameter.Value; \
           done
      

      这假设您的参数中没有空格

      ------------ 编辑 ---------------

      为了能够访问另一个配方中的参数,您可以创建一个规则来创建一个包含一个配方中的文件列表的文件,然后在另一个配方中访问该文件:

      # note that in this case .params-list is actually the name of a file,
      # but it is declared as phony to force it to be rebuilt every time.
      .PHONY: .params-list
      .params-list:
          aws ssm get-parameters-by-path --path /${SERVICE}/${ENV} | jq -c -r '.Parameters[] | .Name' > $@
      
      .PHONY: params-get
      params-get: .params-list
          @for param in $$(cat .params-list); do \
             aws ssm get-parameter --name $${param} --with-decryption | jq -c -r .Parameter.Value; \
           done;
      

      【讨论】:

      • 确实有效。是否可以通过循环进行某种形式的目标组合?主要是这样我可以在其他地方重用目标。
      • 是的,您可以通过在一个配方中输出到文件,然后在另一个配方中导入文件来做到这一点。我也会用这个解决方案编辑答案。
      猜你喜欢
      • 2015-09-28
      • 2016-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 2015-06-08
      • 1970-01-01
      • 2021-12-07
      相关资源
      最近更新 更多