My working unpac repository

Compute C dependencies as GCC intended

C dependencies are intended to be computed as a side-effect of compiling
a C file, not necessarily as a separate step. Compiling a C file with
GCC or clang is now responsible for directly creating the .d dependency
file, if dependencies are being computed. The workaround for MSVC now
becomes two calls to the C compiler, firstly to cl, to compile the file,
then to GCC to create the dependency information. This does the same
work as before, but with the calls in a different order.

Computing dependencies this way allows us to avoid re-checking the C
dependencies on every single invocation of make. If .d files are found,
then they are loaded. On first invocationm, neither the .o or the .d
file will exist, so make knows that the object needs rebuilding without
needing to compute the dependencies of it first.

+115 -88
+1 -1
.github/workflows/build-cross.yml
··· 50 50 set -x 51 51 ./configure --disable-warn-error --disable-ocamldoc \ 52 52 --disable-ocamltest --disable-stdlib-manpages \ 53 - --disable-dependency-generation --prefix="$PREFIX" || failed=$? 53 + --prefix="$PREFIX" || failed=$? 54 54 if ((failed)) ; then set +x 55 55 echo ; echo "::group::config.log content ($(wc -l config.log) lines)" 56 56 cat config.log ; echo '::endgroup::' ; exit $failed
+59 -46
Makefile
··· 1507 1507 $(DEPDIR)/runtime/%.npic.$(D): \ 1508 1508 OC_CPPFLAGS = $(OC_NATIVE_CPPFLAGS) $(ocamlrun_CPPFLAGS) 1509 1509 1510 - ## Compilation of runtime C files 1510 + ## Compilation of C files 1511 + 1512 + # There are two scenarios in which a C object may need to be rebuilt: 1513 + # 1. The C source file is newer than the object 1514 + # 2. A file #include'd by the C source file is newer than the object 1515 + # 1516 + # When the C source file is newer than the object, we do not have to care about 1517 + # which included files are newer, we just have to be sure that all the files 1518 + # which may be #include'd definitely exist. 1519 + # 1520 + # GCC and clang can both generate the precise dependency information 1521 + # needed for (2) as part of compiling the C file. We therefore use C dependency 1522 + # information lazily. 1511 1523 1512 - # The COMPILE_C_FILE macro below receives as argument the pattern 1513 - # that corresponds to the name of the generated object file 1514 - # (without the extension, which is added by the macro) 1515 - define COMPILE_C_FILE 1524 + # All C files must depend on the _presence_ of the $(runtime_BUILT_HEADERS). If 1525 + # a C file actually uses one of these headers, then the dependency will be 1526 + # recorded in the .d file (and becomes a real dependency, not an order-only 1527 + # dependency). 1528 + runtime_MISSING_BUILT_HEADERS = \ 1529 + $(filter-out $(wildcard $(runtime_BUILT_HEADERS)), $(runtime_BUILT_HEADERS)) 1530 + 1531 + # COMPILE_C_FILE generates the compilation rules for C objects 1532 + # $1 = target pattern for the generated object file (without the .$(O)) 1533 + # $2 = source pattern for the C source file (without the .c) 1534 + # $3 = optional; if non-empty suppresses the generation of dependency rules 1535 + define DO_COMPILE_C_FILE 1516 1536 ifeq "$(COMPUTE_DEPS)" "true" 1517 - ifneq "$(if $(3),%,$(1))" "%" 1518 - # -MG would ensure that the dependencies are generated even if the files listed 1519 - # in $$(runtime_BUILT_HEADERS) haven't been assembled yet. However, 1520 - # this goes subtly wrong if the user has the headers installed, 1521 - # as gcc will pick up a dependency on those instead and the local 1522 - # ones will not be generated. For this reason, we don't use -MG and 1523 - # instead include $(runtime_BUILT_HEADERS) in the order only dependencies 1524 - # to ensure that they exist before dependencies are computed. 1525 - $(DEPDIR)/$(1).$(D): runtime/%.c | $(DEPDIR)/runtime $(runtime_BUILT_HEADERS) 1526 - $$(V_CCDEPS)$$(DEP_CC) $$(OC_CPPFLAGS) $$(CPPFLAGS) $$< -MT \ 1527 - 'runtime/$$*$(subst runtime/%,,$(1)).$(O)' -MF $$@ 1528 - endif # ifneq "$(1)" "%" 1529 - $(1).$(O): $(2).c 1537 + # Secondary expansion means we can use @D to depend on the directory being 1538 + # created. 1539 + $(1).$(O): $(2).c \ 1540 + $(if $(3),,| $(DEPDIR)/$$$$(@D) $(runtime_MISSING_BUILT_HEADERS)) 1530 1541 else 1531 1542 $(1).$(O): $(2).c \ 1532 1543 $(runtime_CONFIGURED_HEADERS) $(runtime_BUILT_HEADERS) \ 1533 1544 $(RUNTIME_HEADERS) 1534 1545 endif # ifeq "$(COMPUTE_DEPS)" "true" 1535 1546 $$(V_CC)$$(CC) $$(OC_CFLAGS) $$(CFLAGS) $$(OC_CPPFLAGS) $$(CPPFLAGS) \ 1536 - $$(OUTPUTOBJ)$$@ -c $$< 1547 + $$(OUTPUTOBJ)$$@ \ 1548 + $(if $(3),,$(call DEP_FLAGS,$$@,$(DEPDIR)/$$(@:.$(O)=.$(D)))) \ 1549 + -c $$< 1550 + # This is skipped if either $(COMPUTE_DEPS) is false or $(3) is non-empty 1551 + ifeq "$(COMPUTE_DEPS)$(3)" "true" 1552 + # MSVC doesn't emit usable dependency information, but if GCC is available 1553 + # then it can instead be called in order to generate the .d file. 1554 + ifneq "$(CC)" "$(DEP_CC)" 1555 + $$(V_CCDEPS)$$(DEP_CC) $$(OC_CPPFLAGS) $$(CPPFLAGS) $$< -MM -MT $$@ \ 1556 + -MF $(DEPDIR)/$$(@:.$(O)=.$(D)) 1557 + endif # ifneq "$(CC)" "$(DEP_CC)" 1558 + endif # ifeq "$(COMPUTE_DEPS)$(3)" "true" 1537 1559 endef 1538 1560 1539 - $(DEPDIR)/runtime: 1561 + # The additional call expands the optional $(3) to an empty string 1562 + COMPILE_C_FILE = \ 1563 + $(call DO_COMPILE_C_FILE,$(1),$(2),$\ 1564 + $(if $(filter-out undefined,$(origin 3)),$(3))) 1565 + 1566 + .PRECIOUS: $(DEPDIR)/% 1567 + $(DEPDIR)/%: 1540 1568 $(MKDIR) $@ 1541 1569 1542 1570 runtime_OBJECT_TYPES = % %.b %.bd %.bi %.bpic ··· 1595 1623 1596 1624 ## Runtime dependencies 1597 1625 1598 - runtime_DEP_FILES := $(addsuffix .b, \ 1599 - $(basename $(runtime_BYTECODE_C_SOURCES) runtime/instrtrace)) 1600 - ifeq "$(NATIVE_COMPILER)" "true" 1601 - runtime_DEP_FILES += $(addsuffix .n, $(basename $(runtime_NATIVE_C_SOURCES))) 1602 - endif 1603 - runtime_DEP_FILES += $(addsuffix d, $(runtime_DEP_FILES)) \ 1604 - $(addsuffix i, $(runtime_DEP_FILES)) \ 1605 - $(addsuffix pic, $(runtime_DEP_FILES)) 1606 - runtime_DEP_FILES := $(addsuffix .$(D), $(runtime_DEP_FILES)) 1607 - 1608 - ifeq "$(COMPUTE_DEPS)" "true" 1609 - include $(addprefix $(DEPDIR)/, $(runtime_DEP_FILES)) 1610 - endif 1626 + RUNTIME_DEP_FILES := $(wildcard $(DEPDIR)/runtime/*.$(D)) 1627 + .PHONY: $(RUNTIME_DEP_FILES) 1628 + include $(RUNTIME_DEP_FILES) 1611 1629 1612 1630 .PHONY: runtime 1613 1631 runtime: stdlib/libcamlrun.$(A) ··· 1947 1965 $(eval $(call COMPILE_C_FILE,ocamltest/%.b,ocamltest/%)) 1948 1966 $(eval $(call COMPILE_C_FILE,ocamltest/%.n,ocamltest/%)) 1949 1967 1950 - ifeq "$(COMPUTE_DEPS)" "true" 1951 - include $(addprefix $(DEPDIR)/, $(ocamltest_C_FILES:.c=.$(D))) 1952 - endif 1953 - 1954 - ocamltest_DEP_FILES = $(addprefix $(DEPDIR)/, $(ocamltest_C_FILES:.c=.$(D))) 1955 - 1956 - $(ocamltest_DEP_FILES): | $(DEPDIR)/ocamltest 1957 - 1958 - $(DEPDIR)/ocamltest: 1959 - $(MKDIR) $@ 1960 - 1961 - $(ocamltest_DEP_FILES): $(DEPDIR)/ocamltest/%.$(D): ocamltest/%.c 1962 - $(V_CCDEPS)$(DEP_CC) $(OC_CPPFLAGS) $(CPPFLAGS) $< -MT '$*.$(O)' -MF $@ 1968 + ocamltest_DEPEND_FILES := $(wildcard $(DEPDIR)/ocamltest/*.$(D)) 1969 + .PHONY: $(ocamltest_DEPEND_FILES) 1970 + include $(ocamltest_DEPEND_FILES) 1963 1971 1964 1972 ocamltest/%: CAMLC = $(BEST_OCAMLC) $(STDLIBFLAGS) 1965 1973 ··· 2037 2045 2038 2046 testsuite/tools/poisonedruntime$(EXE): testsuite/tools/poisonedruntime.$(O) 2039 2047 $(V_MKEXE)$(call MKEXE_VIA_CC,$@,$^) 2048 + 2049 + $(eval $(call COMPILE_C_FILE,\ 2050 + testsuite/tools/main_in_c,testsuite/tools/main_in_c,no-deps)) 2051 + $(eval $(call COMPILE_C_FILE,\ 2052 + testsuite/tools/poisonedruntime,testsuite/tools/poisonedruntime,no-deps)) 2040 2053 2041 2054 ocamltest_BYTECODE_LINKFLAGS = -custom -g 2042 2055
+6 -1
Makefile.build_config.in
··· 72 72 INSTALL_OCAMLNAT = @install_ocamlnat@ 73 73 74 74 # The command to generate C dependency information 75 - DEP_CC=@DEP_CC@ -MM 75 + DEP_CC=@DEP_CC@ 76 + ifeq "@compute_deps@-$(CC)" "true-$(DEP_CC)" 77 + DEP_FLAGS = -MMD -MT $(1) -MF $(2) 78 + else 79 + DEP_FLAGS = 80 + endif 76 81 COMPUTE_DEPS=@compute_deps@ 77 82 78 83 BUILD_PATH_LOGICAL = @srcdir_abs@
+28 -12
otherlibs/Makefile.otherlibs.common
··· 166 166 %.cmx: %.ml 167 167 $(V_OCAMLOPT)$(CAMLOPT) -c $(COMPFLAGS) $(OPTCOMPFLAGS) $< 168 168 169 + ifeq "$(COMPUTE_DEPS)" "true" 170 + %.b.$(O): %.c $(REQUIRED_HEADERS) | $(DEPDIR) 171 + $(V_CC)$(CC) $(OC_CFLAGS) $(CFLAGS) $(OC_CPPFLAGS) $(CPPFLAGS) \ 172 + $(OUTPUTOBJ)$@ $(call DEP_FLAGS,$@,$(DEPDIR)/$(@:.$(O)=.$(D))) -c $< 173 + # MSVC doesn't emit usable dependency information, but if GCC is available 174 + # then it can instead be called in order to generate the .d file. 175 + ifneq "$(CC)" "$(DEP_CC)" 176 + $(V_CCDEPS)$(DEP_CC) $(OC_CPPFLAGS) $(CPPFLAGS) $< -MM -MT $@ \ 177 + -MF $(DEPDIR)/$(@:.$(O)=.$(D)) 178 + endif # ifneq "$(CC)" "$(DEP_CC)" 179 + 180 + %.n.$(O): %.c $(REQUIRED_HEADERS) | $(DEPDIR) 181 + $(V_CC)$(CC) $(OC_CFLAGS) $(CFLAGS) \ 182 + $(OC_CPPFLAGS) $(CPPFLAGS) $(OUTPUTOBJ)$@ \ 183 + $(call DEP_FLAGS,$@,$(DEPDIR)/$(@:.$(O)=.$(D))) -c $< 184 + # MSVC doesn't emit usable dependency information, but if GCC is available 185 + # then it can instead be called in order to generate the .d file. 186 + ifneq "$(CC)" "$(DEP_CC)" 187 + $(V_CCDEPS)$(DEP_CC) $(OC_CPPFLAGS) $(CPPFLAGS) $< -MM -MT $@ \ 188 + -MF $(DEPDIR)/$(@:.$(O)=.$(D)) 189 + endif # ifneq "$(CC)" "$(DEP_CC)" 190 + else 169 191 %.b.$(O): %.c $(REQUIRED_HEADERS) 170 192 $(V_CC)$(CC) $(OC_CFLAGS) $(CFLAGS) $(OC_CPPFLAGS) $(CPPFLAGS) \ 171 193 $(OUTPUTOBJ)$@ -c $< 172 194 173 - %.n.$(O): OC_CFLAGS += $(OC_NATIVE_CFLAGS) 174 - 175 195 %.n.$(O): %.c $(REQUIRED_HEADERS) 176 196 $(V_CC)$(CC) $(OC_CFLAGS) $(CFLAGS) \ 177 197 $(OC_CPPFLAGS) $(CPPFLAGS) $(OUTPUTOBJ)$@ -c $< 198 + endif # ifeq "$(COMPUTE_DEPS)" "true" 199 + 200 + %.n.$(O): OC_CFLAGS += $(OC_NATIVE_CFLAGS) 178 201 179 202 ifeq "$(COMPUTE_DEPS)" "true" 180 203 ifneq "$(COBJS_BYTECODE)" "" 181 - include $(addprefix $(DEPDIR)/, $(COBJS_BYTECODE:.$(O)=.$(D))) 182 - ifeq "$(NATIVE_COMPILER)" "true" 183 - include $(addprefix $(DEPDIR)/, $(COBJS_NATIVE:.$(O)=.$(D))) 204 + GENERATED_DEPEND_FILES := $(wildcard $(DEPDIR)/*.$(D)) 205 + .PHONY: $(GENERATED_DEPEND_FILES) 206 + include $(GENERATED_DEPEND_FILES) 184 207 endif 185 208 endif 186 - endif 187 - 188 - $(DEPDIR)/%.b.$(D): %.c | $(DEPDIR) 189 - $(V_CCDEPS)$(DEP_CC) $(OC_CPPFLAGS) $(CPPFLAGS) $< -MT '$*.b.$(O)' -MF $@ 190 - 191 - $(DEPDIR)/%.n.$(D): %.c | $(DEPDIR) 192 - $(V_CCDEPS)$(DEP_CC) $(OC_CPPFLAGS) $(CPPFLAGS) $< -MT '$*.n.$(O)' -MF $@
+13 -15
otherlibs/systhreads/Makefile
··· 84 84 # twice, each time with different options). 85 85 86 86 ifeq "$(COMPUTE_DEPS)" "true" 87 - st_stubs.%.$(O): st_stubs.c 87 + st_stubs.%.$(O): st_stubs.c | $(DEPDIR) 88 88 else 89 89 st_stubs.%.$(O): st_stubs.c $(RUNTIME_HEADERS) $(wildcard *.h) 90 90 endif 91 91 $(V_CC)$(CC) $(OC_CFLAGS) $(CFLAGS) $(OC_CPPFLAGS) $(CPPFLAGS) \ 92 - $(OUTPUTOBJ)$@ -c $< 92 + $(OUTPUTOBJ)$@ $(call DEP_FLAGS,$@,$(DEPDIR)/$(@:.$(O)=.$(D))) -c $< 93 + ifeq "$(COMPUTE_DEPS)" "true" 94 + # MSVC doesn't emit usable dependency information, but if GCC is available 95 + # then it can instead be called in order to generate the .d file. 96 + ifneq "$(CC)" "$(DEP_CC)" 97 + $(V_CCDEPS)$(DEP_CC) $(OC_CPPFLAGS) $(CPPFLAGS) $< -MM -MT $@ \ 98 + -MF $(DEPDIR)/$(@:.$(O)=.$(D)) 99 + endif # ifneq "$(CC)" "$(DEP_CC)" 100 + endif # ifeq "$(COMPUTE_DEPS)" "true" 93 101 94 102 .PHONY: partialclean 95 103 partialclean: ··· 130 138 %.cmx: %.ml 131 139 $(V_OCAMLOPT)$(CAMLOPT) -c $(COMPFLAGS) $(OPTCOMPFLAGS) $< 132 140 133 - DEP_FILES := st_stubs.b.$(D) 134 - ifeq "$(NATIVE_COMPILER)" "true" 135 - DEP_FILES += st_stubs.n.$(D) 136 - endif 137 - 138 141 ifeq "$(COMPUTE_DEPS)" "true" 139 - include $(addprefix $(DEPDIR)/, $(DEP_FILES)) 142 + GENERATED_DEPEND_FILES := $(wildcard $(DEPDIR)/*.$(D)) 143 + .PHONY: $(GENERATED_DEPEND_FILES) 144 + include $(GENERATED_DEPEND_FILES) 140 145 endif 141 146 142 147 %.b.$(D): OC_CPPFLAGS = $(OC_BYTECODE_CPPFLAGS) 143 148 %.n.$(O): OC_CPPFLAGS = $(OC_NATIVE_CPPFLAGS) 144 149 %.n.$(D): OC_CPPFLAGS = $(OC_NATIVE_CPPFLAGS) 145 - 146 - define GEN_RULE 147 - $(DEPDIR)/%.$(1).$(D): %.c | $(DEPDIR) 148 - $$(V_CCDEPS)$$(DEP_CC) $$(OC_CPPFLAGS) $$(CPPFLAGS) $$< -MT '$$*.$(1).$(O)' -MF $$@ 149 - endef 150 - 151 - $(foreach object_type, b n, $(eval $(call GEN_RULE,$(object_type)))) 152 150 153 151 .PHONY: depend 154 152 depend:
+1 -3
tools/ci/actions/runner.sh
··· 61 61 --enable-flambda-invariants \ 62 62 --enable-ocamltest \ 63 63 --enable-native-toplevel \ 64 - --disable-dependency-generation \ 65 64 -C $CONFIG_ARG 66 65 } 67 66 ··· 300 299 local failed 301 300 trap ReportBuildStatus ERR 302 301 303 - call-configure --disable-dependency-generation \ 304 - --disable-debug-runtime \ 302 + call-configure --disable-debug-runtime \ 305 303 --disable-instrumented-runtime \ 306 304 --enable-ocamltest \ 307 305
+6 -8
tools/ci/appveyor/appveyor_build.sh
··· 77 77 78 78 case "$1" in 79 79 cygwin*) 80 - args+=('--disable-dependency-generation' '--enable-native-toplevel');; 80 + args+=('--enable-native-toplevel');; 81 81 mingw32) 82 - args+=('--host=i686-w64-mingw32' '--disable-dependency-generation');; 82 + args+=('--host=i686-w64-mingw32');; 83 83 mingw64) 84 - args+=('--host=x86_64-w64-mingw32' '--disable-dependency-generation' \ 85 - '--disable-stdlib-manpages' '--enable-native-toplevel');; 84 + args+=('--host=x86_64-w64-mingw32' '--disable-stdlib-manpages' \ 85 + '--enable-native-toplevel');; 86 86 msvc32) 87 - args+=('--host=i686-pc-windows' '--disable-dependency-generation');; 87 + args+=('--host=i686-pc-windows');; 88 88 msvc64) 89 - # Explicitly test dependency generation on msvc64 90 - args+=('--host=x86_64-pc-windows' '--enable-dependency-generation' \ 91 - '--enable-native-toplevel');; 89 + args+=('--host=x86_64-pc-windows' '--enable-native-toplevel');; 92 90 esac 93 91 if [[ $RELOCATABLE = 'true' ]]; then 94 92 args+=('--with-relative-libdir' \
-1
tools/ci/inria/light
··· 83 83 --disable-shared \ 84 84 --disable-debug-runtime \ 85 85 --disable-instrumented-runtime \ 86 - --disable-dependency-generation \ 87 86 --disable-ocamldoc \ 88 87 --disable-stdlib-manpages 89 88
+1 -1
tools/ci/inria/step-by-step-build/script
··· 23 23 # Make sure the repository is clean 24 24 git clean -q -f -d -x 25 25 26 - ./configure --prefix "$instdir" --disable-dependency-generation 26 + ./configure --prefix "$instdir" 27 27 make $jobs world 28 28 make $jobs opt 29 29 make $jobs opt.opt