在 Makefile 中生成并检查下载文件校验和

Generate and check download file checksum in Makefile

作为我的 makefile 的一部分,我需要下载并构建 ZLib。但是我想确保当我下载 ZLib 时,通过将下载的 .tar.gz 的 sha256 与已知的正确 sha256 值进行比较来确保它是正确的。这需要在多个平台上工作。

到目前为止,我有类似下面的内容,但是当我将它与 ZLIB_SHA256 进行比较时,ZLIB_SHA256_ACTUAL 的值似乎总是空白,所以我的 makefile 总是以错误退出,因为校验和不一样。我是 Makefile 的新手,有人可以告诉我我做错了什么吗?

ZLIB_VER = 1.2.11
ZLIB_SHA256 = c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1

SHA256_CMD = sha256sum
ifeq ($(PLATFORM), OS_MACOSX)
        SHA256_CMD = openssl sha256 -r
endif
ifeq ($(PLATFORM), OS_SOLARIS)
        SHA256_CMD = digest -a sha256
endif

libz.a:
        -rm -rf zlib-$(ZLIB_VER)
        curl -O -L http://zlib.net/zlib-$(ZLIB_VER).tar.gz
        ZLIB_SHA256_ACTUAL = $(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz
        ifneq ($(ZLIB_SHA256), $(ZLIB_SHA256_ACTUAL))
                $(error zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected="$(ZLIB_SHA256)" actual="$(ZLIB_SHA256_ACTUAL)")
        endif
        tar xvzf zlib-$(ZLIB_VER).tar.gz
        cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC' ./configure --static && make
        cp zlib-$(ZLIB_VER)/libz.a .

一个 makefile 在一个文件中包含两种不同的编程语言。大多数文件使用 makefile 语法,make 可以理解和解析。但是规则的食谱使用 shell 语法,make 不会尝试解释:它只是将食谱的内容传递给 shell 来解释。

配方是 makefile 的一部分,在目标定义之后用制表符缩进。因此,在上面的示例中,目标定义是 libz.a: 并且之后所有用 TAB 缩进的行都是配方行。它们由 make.

传递给 shell,而不是 运行

食谱是一行;您不能将配方行与 makefile 行穿插在一起。一旦 make 看到第一个 non-recipe 行,这就是配方的结尾,make 开始将剩余的行视为 makefile 行。

让我们看看你的规则:

libz.a:
        -rm -rf zlib-$(ZLIB_VER)
        curl -O -L http://zlib.net/zlib-$(ZLIB_VER).tar.gz

好的,这很好:您已经创建了一个目标 libz.a 并在您的配方中提供了两个有效的 shell 命令命令行。

        ZLIB_SHA256_ACTUAL = $(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz

好的,现在你有问题了;这是一个 make 变量赋值,而不是 shell 命令,但是因为你已经用 TAB 缩进了它,所以 make 不会解释它:make 只会将它传递给 shell。那不是有效的 shell 命令(在 shell 中,变量赋值不能在等号两边有空格);这是试图 运行 一个字面上命名为 ZLIB_SHA256_ACTUAL 的程序,并将参数 =SHA256_CMD 变量的扩展传递给它。即使这被识别为 make 赋值,它也不会执行您想要的操作,因为它只会将变量的值设置为字符串 openssl sha256 -r zlib-1.2.11.tar.gz: you want to 运行 that command and set the variable到输出。

然后下一行:

        ifneq ($(ZLIB_SHA256), $(ZLIB_SHA256_ACTUAL))
                $(error zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected="$(ZLIB_SHA256)" actual="$(ZLIB_SHA256_ACTUAL)")
        endif

同样,这是错误的,因为这些是 make 命令,但您已将它们放入配方中,这意味着它们将被传递给 shell,但是shell 对他们一无所知。

但是,它们从来没有机会被传递给 shell,因为 make 在将配方发送给 shell 之前对配方所做的一件事是展开所有 make 变量和函数.因此,当 make 扩展它时 运行 是 error 函数,它立即失败并且 make 永远没有机会尝试 运行 配方。

这是 make 的棘手部分。也许我只是把你和上面的所有东西搞混了。

简短的回答是:您必须使用 shell 命令来执行配方中的操作。你不能使用 make 命令(如 ifeq 等),如果你想在配方中设置变量,它们必须是 shell 个变量,而不是 生成 个变量。

所以,你想要这样的东西,它使用 shell 语法而不是 make 变量赋值和测试语法。

EDIT 请注意,您的 SHA 生成命令不仅会打印 SHA,还会打印文件名,因此您不能将它们作为字符串进行比较:它们会永远不一样。您需要做一些更有趣的事情;有很多方法可以解决这个问题。这里我决定用case来做比较:

libz.a:
        -rm -rf zlib-$(ZLIB_VER)
        curl -O -L http://zlib.net/zlib-$(ZLIB_VER).tar.gz
        ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz`; \
        case "$$ZLIB_SHA256_ACTUAL " in \
            ($(ZLIB_SHA256)\ *) : ok ;; \
            (*) echo zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected=\"$(ZLIB_SHA256)\" actual=\"$$ZLIB_SHA256_ACTUAL\"; \
            exit 1 ;; \
        esac
        tar xvzf zlib-$(ZLIB_VER).tar.gz
        cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC' ./configure --static && $(MAKE)
        cp zlib-$(ZLIB_VER)/libz.a .

请注意,配方中的每个逻辑行都会传递给 shell 的一个新实例,因此如果您想设置一个 shell 变量并测试其值,您必须组合物理行使用 backslash/newline 语法合并为一个逻辑行。

此外,当 运行在食谱中使用 sub-make 时,您应该始终使用变量 $(MAKE) 而永远不要只使用 make.