bfieck bfieck - 2 months ago 35
TypeScript Question

Makefile compiles all tsc regardless of changes

Question



I wrote a makefile to compile all of my typescript files to javascript, but also run them through a linter (tslint). I wrote the following 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;)


The problem is that any time I run
make
(or equivalently
make all
), the scss and tslint rules only run on changed files as appropriate, but even if I've changed no typescript files, the tsc recipe runs it's command (
$(TSC) $(TSC_FLAGS) $(TS_SRC)
). What did I do wrong?

Note



From what I understand of typescript, even if only one file was changed, all the files need to be recompiled so it can understand references that they make between them, which is fair I suppose. With that in mind, this makefile needs to run
tslint
on each individual typescript source file, but also must run
tsc
on all of the source typescript files if even just one of them is changed. (currently it does that, the problem is just that
tsc
runs even if nothing's changed)

Bonus



You might wonder why I left the sass/scss stuff in at all, it's because every
%.scss
file in
./apps/
@import
s my main scss file (
$(MAIN_SASS_SRC)
) and I'd like it to run something that first checks if that's been changed, and if so
touch
es all the app scuzzy files so they re-import it but regardless runs the sass/scss recipe AFTER that has completed. I can't think of a way to do this without a race condition (since I'm using
-j
) that doesn't involve running make twice or without submakes. In general, I'm pretty new to this, so I'd happily accept criticisms on any part of this makefile in the comments, as well as a solution to that specific problem.

Answer

You've told make you're creating the file tsc, but the recipe doesn't create it.

Use the output files themselves as dependencies

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

.PHONY: all js

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 $@