1

是否可以同时将一个规则依赖于两个文件,并在其中一个文件发生更改时仅执行一次规则?

我正在将 SASS 源文件的编译添加到我的 Makefile 中。我用 Compass 编译它们,但它有自己的配置,其中指定了源文件夹和目标文件夹。Compass 不带任何参数执行,并将所有更改的 SASS 文件编译成相应的 CSS 文件。

假设我有两个 SASS 文件:

folder1/file1.scss
folder2/file2.scss

如果两个文件都改变了,只执行一次 compass 编译两个文件,不需要执行两次 Compass。我可以创建一个 Makefile,如果其中一个或两个文件都被更改,我只能在其中执行一次编译吗?说这样的话:

folder_out1/file1.css folder_out2/file2.css: folder1/file1.scss folder2/file2.scss
  compass compile

编辑- 适用于 FreeBSD 的修正脚本(有一些小的改动):

#!/usr/bin/env bash

# Start fresh: delete all files and folders
rm -rf folder folder_out Makefile.mock mock_compass
# Create the input folder and files
mkdir folder
echo 1 > folder/file1.scss
echo 2 > folder/file2.scss

# Create mock compass command that creates output directories and copies
# input files to output files without transforming them
echo '
#!/usr/bin/env bash

if ! [ -d folder_out ]; then
    mkdir folder_out
fi
cp folder/file1.scss folder_out/file1.css
cp folder/file2.scss folder_out/file2.css' > ./mock_compass
chmod +x mock_compass

# Create the Makefile
echo -e '
# Disable implicit rules to simplify debugging output
.SUFFIXES:
%: %,v
%: RCS/%
%: RCS/%,v
%: s.%
%: SCCS/s.%

folder_out/file1.css folder_out/file2.css: folder/file1.scss folder/file2.scss
\t@echo Running compass
\t./mock_compass
' > Makefile.mock

# Test it!

echo 'Initial build:'
gmake -f Makefile.mock
echo

echo 'Changing only file1'
touch folder/file1.scss
gmake -f Makefile.mock
echo

echo 'Changing only file2'
touch folder/file2.scss
gmake -f Makefile.mock
echo

echo 'Changing both'
touch folder/file1.scss folder/file2.scss
gmake -f Makefile.mock
echo
4

1 回答 1

2

是的,这两行本身就是一个正确的 Makefile,可以完全按照您的意愿行事。

但是,您是否有机会在 Mac 上执行此操作?不幸的是,Mac 文件系统只有 1 秒的文件时间戳分辨率,在这种情况下可能会对 Make 造成严重破坏。

这是一个测试脚本:

#!/bin/bash

# Start fresh: delete all input and output folders
rm -rf folder{1,2,_out{1,2}}
# Create input folders and files
mkdir folder1
mkdir folder2
echo 1 > folder1/file1.scss
echo 2 > folder2/file2.scss

# Create mock compass command that creates output directories and copies
# input files to output files without transforming them
echo '
if ! [ -d folder_out1 ]; then
    mkdir folder_out{1,2}
fi
cp folder1/file1.scss folder_out1/file1.css
cp folder2/file2.scss folder_out2/file2.css' > ./mock_compass
chmod +x mock_compass

# Create the Makefile
echo '
# Disable implicit rules to simplify debugging output
.SUFFIXES:
%: %,v
%: RCS/%
%: RCS/%,v
%: s.%
%: SCCS/s.%

folder_out1/file1.css folder_out2/file2.css: folder1/file1.scss folder2/file2.scss
    @echo Running compass
    ./mock_compass
' > Makefile.mock

# Test it!

echo 'Initial build:'
make -f Makefile.mock
echo

echo 'Changing only file1'
touch folder1/file1.scss
make -f Makefile.mock
echo

echo 'Changing only file2'
touch folder2/file2.scss
make -f Makefile.mock
echo

echo 'Changing both'
touch folder1/file1.scss folder2/file2.scss
make -f Makefile.mock
echo

一切在技术上都是正确的,但是当你在 Mac 上运行它时,你会得到不正确的输出:

Initial build:
Running compass
./mock_compass

Changing only file1
make[1]: `folder_out1/file1.css' is up to date.

Changing only file2
make[1]: `folder_out1/file1.css' is up to date.

Changing both
make[1]: `folder_out1/file1.css' is up to date.

输入文件在创建输出文件后更改,但在同一秒内,因此它们的创建顺序不会保存在磁盘上。当 Make 重新运行时,它会看到两者完全相同的时间戳,并假设它们是最新的。您可以通过ls -lT在脚本中添加调用并将-d标志添加到 Make 运行来验证这一点。

一个 hacky 解决方法是将mock_compass调用更改为:

./mock_compass && sleep 1 # mac timestamp resolution is 1 second :(

然后导致测试脚本产生正确的输出:

Initial build:
Running compass
./mock_compass && sleep 1 # mac timestamp resolution is 1 second :(

Changing only file1
Running compass
./mock_compass && sleep 1 # mac timestamp resolution is 1 second :(

Changing only file2
Running compass
./mock_compass && sleep 1 # mac timestamp resolution is 1 second :(

Changing both
Running compass
./mock_compass && sleep 1 # mac timestamp resolution is 1 second :(

但也将脚本的运行时间从 0 秒增加到 4 秒 :'(

如果您对此有更好的解决方法,请告诉我!

于 2013-01-28T18:16:02.823 回答