make对表达式的评估不同于shell

如何解决make对表达式的评估不同于shell

我正在尝试检查地址A上存在的文件副本是否比地址B上存在的原始文件更早。因此,例如,考虑一个名为scratch的文件与该文件存在于同一文件夹中。包含makefile的一个。让这些文件的地址为address_a/。现在,让另一个地址,例如address_b/bin,我要在其中安装 scratch。但是,我打算在2种情况下执行安装:

  • address_b/bin不包含scratch
  • 或者只要scratch中存在的address_b/bin副本早于address_a/中存在的副本,就可以。

为此,我使用以下shell命令:

[ address_b/bin/scratch -ot address_a/scratch ] && echo 1 || echo 0

现在直接在终端上执行此命令将得到1作为输出。但是,通过makefile执行此命令将导致输出0。我使用此命令的方法如下:

.PHONY : install
INSTALL = install
source = scratch
destination = address_b/bin/

install : capture_status != [ $(destination) -ot $(source) ] && echo 1 || echo 0
install :
    @echo $(capture_status)
ifeq ($(capture_status),1)
    $(INSTALL) $(source) $(destination)
else
    @echo 'Installation has already been done.'
endif

那么,问题出在哪里?

注意:我正在Ubuntu 18.04.3上使用GNU Make 4.1

解决方法

您知道make的主要作用是比较目标文件和必备文件的最后修改时间,以便确定是否必须重新制作目标文件吗?在您的情况下,我认为,以下内容可以更自然地满足您的需求:

.PHONY : install
INSTALL = install
source = scratch
destination = address_b/bin/

install: $(destination)/$(source)

$(destination)/$(source): $(source)
    $(INSTALL) $< $(dir $@)

总而言之,它说要做到:

  1. install$(destination)/$(source)的别名,不是真实文件。

  2. $(destination)/$(source)取决于$(source),也就是说,如果$(destination)/$(source)早于$(source),则必须重新制作,否则必须更新。日期。

  3. 如果必须重新制作$(destination)/$(source)(因为它早于$(source)),则要执行的shell命令为:

     $(INSTALL) $< $(dir $@)
    

$<是一个make自动变量,其扩展为规则的第一个前提条件(在这种情况下为$(source)$@扩展为目标($(destination)/$(source)),并且$(dir ...)是一个返回父目录的make函数。因此,该命令等效于:

    $(INSTALL) $(source) $(destination)

但更通用。

编辑1(未完成安装时添加自定义消息)

如果您想在$(destination)/$(source)最新时使用特定的消息或操作,则可以使用空文件(例如.done)将一条规则中的信息传递给另一条规则:

.PHONY : install
INSTALL = install
source = scratch
destination = address_b/bin/
DONE    := .done
MESSAGE := custom message

install: $(destination)/$(source)
   @[ -f $(DONE) ] && rm $(DONE) || echo "$(MESSAGE)"

$(destination)/$(source): $(source)
    $(INSTALL) $< $(dir $@)
    @touch $(DONE)

编辑2(添加观察到的行为的解释)

您观察到的问题是由于make做什么而引起的。 ifeq条件句在解析Makefile时由make评估,而稍后扩展目标特定的变量时,则改变条件条件的条件为时已晚...演示:

$ cat Makefile
.PHONY: c

c: foo := bar

c:
ifeq ($(foo),bar)
    @echo "foo = bar and foo = $(foo)"
else
    @echo "foo != bar and foo = $(foo)"
endif
$ make c
foo != bar and foo = bar

编辑3(使if为条件条件和shell为条件条件)

下面的两个其他解决方案根本不是我的最爱,因为使用Shell而不是make来测试文件是否比另一个文件更旧会冒犯我的敏感性(并且我很敏感)。他们在这里仅供参考。

GNU make还有其他条件,例如$(if ...)函数,您可以使用所需的方式,因为它们仅在make将特定配方变量扩展到make将配方传递给shell之前扩展。

注意:在$(if ...)测试空度时,我修改了capture_status的分配方式,使其值为1或空字符串而不是1或0。

注意:由于您的原始版本无效,我在测试中将$(destination)替换为$(destination)/$(source),我认为:目录修改时间与文件修改时间不同。

.PHONY : install
INSTALL = install
source = scratch
destination = address_b/bin/

install : capture_status != [ $(destination)/$(source) -ot $(source) ] && echo 1
install :
    @echo $(capture_status)
    $(if $(capture_status),$(INSTALL) $(source) $(destination),@echo 'Installation has already been done.')

但是shell条件语句的工作原理几乎相同:

.PHONY : install
INSTALL = install
source = scratch
destination = address_b/bin/

install : capture_status != [ $(destination)/$(source) -ot $(source) ] && echo 1 || echo 0
install :
    @echo $(capture_status)
    @if [ $(capture_status) -eq 1 ]; then \
        echo "$(INSTALL) $(source) $(destination)"; \
        $(INSTALL) $(source) $(destination); \
    else \
        echo 'Installation has already been done.'; \
    fi

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-