仅在其中一个文件发生更改时执行目标

时间:2013-01-28 15:50:24

标签: makefile sass gnu-make compass-sass

是否可以同时将一个规则依赖于两个文件,并且当这些文件中的任何一个发生更改时,只执行一次规则?

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

说我有两个SASS文件:

folder1/file1.scss
folder2/file2.scss

如果两个文件都已更改,只执行一次罗盘编译两次文件,则无需执行两次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

1 个答案:

答案 0 :(得分:1)

是的,这两行本身就是一个正确的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秒:'(

如果您有更好的工作要求,请告诉我!