diff --git a/data/Makefile.in b/data/Makefile.in index 4348ba85..bcfd3736 100644 --- a/data/Makefile.in +++ b/data/Makefile.in @@ -62,11 +62,10 @@ DEV_CUOBJ += $(addprefix $(OBJ_DIR)/,$(notdir $(CUSOURCE:%.cpp=%_cu_dev.o) VPATH += $(dir $(CUSOURCE)) NVCC_BIN := nvcc -NVCC := $(shell which $(NVCC_BIN)) -CUDA_DIR := $(dir $(NVCC))/.. - HAS_CUDA := $(shell command -v $(NVCC_BIN) 2> /dev/null) ifdef HAS_CUDA +NVCC := $(shell which $(NVCC_BIN) 2>/dev/null) +CUDA_DIR := $(dir $(NVCC))/.. NVCC_CFLAGS += --std=c++11 NVCC_CFLAGS += -ccbin=g++ NVCC_CFLAGS += $(DEVCAP) @@ -134,7 +133,8 @@ INTEL := icpc GCC := g++ ifndef CUDA_ONLY -COMPILERS := $(foreach c, GCC INTEL CLANG, $(if $(shell which $($(c))), $c,)) +COMPILERS := $(foreach c, GCC INTEL CLANG, \ + $(if $(shell which $($(c)) 2>/dev/null), $c,)) endif ifdef CLANG_ONLY @@ -147,7 +147,7 @@ RESULTS_DIR := results # on systems with non-standard gcc installations (such as module), clang may # be unable to determine the correct gcc toolchain -GCC_TOOLCHAIN := $(dir $(shell which $(GCC)))/.. +GCC_TOOLCHAIN := $(dir $(shell which $(GCC) 2>/dev/null))/.. CLANG_REQUIRED := --gcc-toolchain=$(GCC_TOOLCHAIN) # Compiler setting targets @@ -363,7 +363,7 @@ $(OBJ_DIR)/%_$(R_ID).o: %.cpp Makefile custom.mk | $(OBJ_DIR) -DFLIT_FILENAME='"$(R_ID)"' # Otherwise, we're not in a recursion. -else +else # ifndef R_IS_RECURSED $(OBJ_DIR): mkdir -p $(OBJ_DIR) @@ -387,7 +387,11 @@ TARGETS += $$(RESULTS_DIR)/$(strip $1)_$$(HOSTNAME)_$(strip $3)_$(strip # TODO: use the variable $$(MAKECMDGOALS) to get the original make target # or see if it is even necessary -$$(RESULTS_DIR)/$(strip $1)_$$(HOSTNAME)_$(strip $3)_$(strip $2): | $$(OBJ_DIR) $$(RESULTS_DIR) +# Make the recursive target depend on $$(GT_TARGET), not because it actually +# depends on that target, but we know that when $$(GT_TARGET) needs to be +# rebuilt, so does each recursive target. + +$$(RESULTS_DIR)/$(strip $1)_$$(HOSTNAME)_$(strip $3)_$(strip $2): $$(GT_TARGET) | $$(OBJ_DIR) $$(RESULTS_DIR) -$$(MAKE) rec \ R_IS_RECURSED=True \ R_CUR_COMPILER=$(strip $1) \ @@ -484,6 +488,10 @@ $(TARGETS): $(FLIT_LIB_DIR)/libflit.so $(CUTARGETS): $(FLIT_LIB_DIR)/libflit.so endif # ifeq ($(UNAME_S),Darwin): meaning, we are on a mac +# include the build dependencies for gt and dev + +-include $(GT_DEPS) +-include $(DEV_DEPS) # # Now for the compilation rules: diff --git a/tests/flit_makefile/tst_empty_project.py b/tests/flit_makefile/tst_empty_project.py index e53f16cc..05267d89 100644 --- a/tests/flit_makefile/tst_empty_project.py +++ b/tests/flit_makefile/tst_empty_project.py @@ -50,6 +50,8 @@ Let's actually now compile and run the empty test under different circumstances +TODO: this takes too long to run. Can we do something faster? + >>> import subprocess as subp >>> with th.tempdir() as temp_dir: ... th.flit.main(['init', '-C', temp_dir]) # doctest:+ELLIPSIS diff --git a/tests/flit_makefile/tst_incremental_build.py b/tests/flit_makefile/tst_incremental_build.py new file mode 100644 index 00000000..46524ab2 --- /dev/null +++ b/tests/flit_makefile/tst_incremental_build.py @@ -0,0 +1,107 @@ +''' +Tests FLiT's generated Makefile and its ability to correctly do incremental +builds. + +The tests are below using doctest + +>>> import glob +>>> import os +>>> import subprocess as subp +>>> import re +>>> import time + +Test that the dev target will be rebuilt if one of the files is updated. +We use 'make --touch' to simply touch files it would create and then we will +test that the correct files are updated. +>>> def compile_target(directory, target): +... 'Compiles the dev target using "make --touch" and returns the output' +... output = subp.check_output(['make', '-C', directory, '--touch', target]) +... return output.decode('utf-8').strip() +>>> compile_dev = lambda x: compile_target(x, 'dev') +>>> with th.tempdir() as temp_dir: +... th.flit.main(['init', '-C', temp_dir]) # doctest:+ELLIPSIS +... # fake the built elements -- don't actually build for time's sake +... os.mkdir(os.path.join(temp_dir, 'obj')) +... before_build = compile_dev(temp_dir) +... # this next build should say nothing to do +... after_build = compile_dev(temp_dir) +... # update one file and recompile +... with open(os.path.join(temp_dir, 'main.cpp'), 'a') as mainfile: +... mainfile.write('#include "new_header.h"\\n') +... with open(os.path.join(temp_dir, 'obj', 'main_dev.d'), 'w') as maindep: +... maindep.write('obj/main_dev.o: main.cpp new_header.h\\n') +... th.touch(os.path.join(temp_dir, 'new_header.h')) +... after_modify = compile_dev(temp_dir) +... # touch the header file and make sure it recompiles again +... time.sleep(0.001) # give some time before touching again +... th.touch(os.path.join(temp_dir, 'new_header.h')) +... after_touch = compile_dev(temp_dir) +Creating ... + +>>> def touched_files(outstring): +... 'Returns list of touched files in sorted order' +... return sorted([x[6:] for x in outstring.splitlines() +... if x.startswith('touch ')]) + +Make sure all of the correct files were created with our build commands + +>>> touched_files(before_build) +['devrun', 'obj/Empty_dev.o', 'obj/main_dev.o'] + +>>> touched_files(after_build) +[] + +>>> touched_files(after_modify) +['devrun', 'obj/main_dev.o'] + +>>> touched_files(after_touch) +['devrun', 'obj/main_dev.o'] + +Now, let's test the same thing with the "gt" target + +>>> compile_gt = lambda x: compile_target(x, 'gt') +>>> with th.tempdir() as temp_dir: +... th.flit.main(['init', '-C', temp_dir]) # doctest:+ELLIPSIS +... # fake the built elements -- don't actually build for time's sake +... os.mkdir(os.path.join(temp_dir, 'obj')) +... before_build = compile_gt(temp_dir) +... # this next build should say nothing to do +... after_build = compile_gt(temp_dir) +... # update one file and recompile +... with open(os.path.join(temp_dir, 'main.cpp'), 'a') as mainfile: +... mainfile.write('#include "new_header.h"\\n') +... with open(os.path.join(temp_dir, 'obj', 'main_gt.d'), 'w') as maindep: +... maindep.write('obj/main_gt.o: main.cpp new_header.h\\n') +... th.touch(os.path.join(temp_dir, 'new_header.h')) +... after_modify = compile_gt(temp_dir) +... # touch the header file and make sure it recompiles again +... time.sleep(0.001) # give some time before touching again +... th.touch(os.path.join(temp_dir, 'new_header.h')) +... after_touch = compile_gt(temp_dir) +Creating ... + +Make sure all of the correct files were created with our build commands + +>>> touched_files(before_build) +['gtrun', 'obj/Empty_gt.o', 'obj/main_gt.o'] + +>>> touched_files(after_build) +[] + +>>> touched_files(after_modify) +['gtrun', 'obj/main_gt.o'] + +>>> touched_files(after_touch) +['gtrun', 'obj/main_gt.o'] +''' + +# Test setup before the docstring is run. +import sys +before_path = sys.path[:] +sys.path.append('..') +import test_harness as th +sys.path = before_path + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/tests/test_harness.py b/tests/test_harness.py index b5cb7983..a0036516 100644 --- a/tests/test_harness.py +++ b/tests/test_harness.py @@ -112,6 +112,14 @@ def tempdir(*args, **kwargs): yield new_dir shutil.rmtree(new_dir) +def touch(filename): + ''' + Create an emtpy file if it does not exist, otherwise updates the + modification time. + ''' + from pathlib import Path + Path(filename).touch() + flit = _path_import(_script_dir, 'flit') config = _path_import(_script_dir, 'flitconfig')