Makefile 编译所有 tsc 而不管更改

Makefile compiles all tsc regardless of changes

问题

我写了一个 makefile 来将我所有的 typescript 文件编译成 javascript,而且 运行 它们通过 linter (tslint)。我写了下面的 makefile:

#binaries
SASS=./node_modules/node-sass/bin/node-sass
TSC=tsc
TSLINT=tslint

#source dirs
SASS_DIRS=./apps ./lib
TS_DIRS=./apps ./lib ./library

#source files
SASS_SRC ::=  $(shell find $(SASS_DIRS) -name '*.scss')
TS_SRC ::= $(shell find $(TS_DIRS) -name '*.ts')
APPS_SASS_SRC ::= $(shell find ./apps -name 'app.scss')
MAIN_SASS_SRC=./library/incode/incode.scss
MAIN_SASS_DEST=./library/incode/incode.css

#compilation options/binary flags
SASS_FLAGS = --output-style compressed -q
TSC_FLAGS = --sourceMap -t ES5 --noEmitOnError

.PHONY: clean

all: tsc sass

sass scss: $(patsubst %.scss, %.css, $(SASS_SRC))

%.css: %.scss
    $(SASS) $(SASS_FLAGS) $< > $@

tsc: $(patsubst %.ts, %.js, $(TS_SRC))
    $(TSC) $(TSC_FLAGS) $(TS_SRC)

%.js: %.ts
    $(TSLINT) $<

incodesass: $(MAIN_SASS_SRC)
     touch $(APPS_SASS_SRC); $(SASS) $(SASS_FLAGS) $< > $(MAIN_SASS_DEST); make sass

clean:
    $(shell for i in $(shell find -name '*.rej'); do rm $i; done; for i in $(shell find -name '*.orig'); do rm $i; done;)

问题是,任何时候我 运行 make(或等同于 make all),scss 和 tslint 规则仅 运行 适当地更改文件,但是即使我没有更改任何打字稿文件,tsc 配方 运行 也是命令 ($(TSC) $(TSC_FLAGS) $(TS_SRC))。我做错了什么?

备注

据我对打字稿的了解,即使只更改了一个文件,所有文件都需要重新编译,以便它可以理解它们之间的引用,我想这是公平的。考虑到这一点,这个 makefile 需要 运行 tslint 每个 个人 typescript 源文件,而且还必须 运行 tsc 所有 源打字稿文件,即使其中一个被更改。 (目前它确实如此,问题只是 tsc 运行s 即使没有任何改变)

奖金

你可能想知道为什么我把 sass/scss 的东西留在里面,这是因为 ./apps/ @import 中的每个 %.scss 文件都是我的主 scss 文件($(MAIN_SASS_SRC)) 并且我希望它成为 运行 首先检查是否已更改的东西,如果是 touches 所有应用程序 scuzzy 文件所以他们重新导入它但不管 运行s sass/scss 食谱完成后。我想不出没有竞争条件(因为我使用的是 -j)不涉及 运行ning make 两次或没有 submakes 的方法。总的来说,我对此很陌生,所以我很乐意接受评论中对此 makefile 任何部分的批评,以及针对该特定问题的解决方案。

您已告知 make 您正在创建文件 tsc,但配方并未创建它。

使用输出文件本身作为依赖项

SASS_CSS := $(SASS_SRC:=.scss=.css)
TS_JS    := $(TS_SRC:.ts=.js)

.PHONY: all js.stub

all: $(SASS_CSS) $(TS_JS)

%.css: %.scss
    $(SASS) $(SASS_FLAGS) $< > $@

$(TS_JS): %.js: %.ts js.stub
    $(TSLINT) $<

js.stub: $(TS_SRC)
    $(TSC) $(TSC_FLAGS) $^
    @touch $@

我知道我已经接受了一个答案,但我认为值得一提的是,还有一种不同的方法可以实现这一点。

#binaries
TSC=tsc
TSLINT=tslint

#source dir
TS_DIRS=./apps ./lib ./library

#source files
TS_SRC ::= $(shell find $(TS_DIRS) -name '*.ts')

#compilation options/binary flags
TSC_FLAGS = --sourceMap -t ES5 --noEmitOnError

#outfiles
JS_OUT ::= $(patsubst %.ts, %.js, $(TS_SRC))

.PHONY: all

all: $(word 1, $(JS_OUT))

$(word 1, $(JS_OUT)): $(TS_SRC)
    $(TSLINT) $?
    $(TSC) $(TSC_FLAGS) $(TS_SRC)

我大学的一位教授帮助我解决了这个问题。由于每当单个 .ts 源文件更改时,所有这些文件都需要重建,我可以将我的目标设为 javascript 输出文件中的任何一个。那样的话,我告诉 make the truth,这条规则将产生它的目标。 $? 获取已更改的配方的所有依赖项,因此只有已更改的文件通过 linter。相比之下,第二行直接编译了所有的源文件。

这样做的一个额外好处是,虽然 make -j 将为每个目标线程化每个配方,但构建必须按顺序执行,因此相对冗长的 tsc 步骤不会发生,如果一个的文件无法 lint,有效地提高了 makefile 的平均执行时间(因为我是一个总是搞砸的白痴)

奖金

我知道在 post 中提出多个问题可能会让人不悦,但我已经这样做了,既然我有了答案,我觉得有义务分享它。重建所有 .css 文件实际上非常简单:

#binaries
SASS=./node_modules/node-sass/bin/node-sass

#source dir
SASS_DIRS=./apps
#I used to rebuild './lib' every time, not necessary as it turns out.

#source files
SASS_SRC ::=  $(shell find $(SASS_DIRS) -name '*.scss')
MAIN_SASS_SRC=./library/incode/incode.scss
MAIN_SASS_DEST=./library/incode/incode.css

#compilation options/binary flags
SASS_FLAGS = --output-style compressed -q

.PHONY: all

all: sass

sass: $(patsubst %.scss, %.css, $(SASS_SRC))

%.css: %.scss $(MAIN_SASS_DEST)
    $(SASS) $(SASS_FLAGS) $< > $@

$(MAIN_SASS_DEST): $(MAIN_SASS_SRC)
    $(SASS) $(SASS_FLAGS) $< > $(MAIN_SASS_DEST)

因为 每个 apps/*.scss 文件都是 @import 我的主 scuzzy 文件,我所要做的就是让每个文件都依赖于主文件.坦率地说,我对自己在没有帮助的情况下无法解决这个问题感到失望。