Skip to content

Commit ad907d5

Browse files
committed
Add Makefile caching helpers for lazy, expensive evaluations
Makefiles natively offer two variable expansion modes: immediate and deferred. When expanding variables that require invocations of external programs (such as `llvm-config`) immediate expansion is almost always preferred, as it will run the external command once, exactly when the makefile variable is defined. Deferred mode, on the other hand, will expand the variable every time it is used, running the external program again and again. When the external program is expensive, this cost can slow down the build significantly, however when the external program requires some setup (for instance, when it itself is downloaded through other rules in the Makefile) it cannot always be immediately expanded. To address this, we build a caching layer that allows for deferred expansion, but once it has been expanded once, the variable is replaced with the result of running the command, and further hits to the same variable will return the cached value. (With the slight caveat that an empty result will cause the external command to be run again in the future). As an example usecase, this commit converts our relative path calculation to use a python script and showcases how to cache this operation. This will be used for further JLL stdlib work where the invocation is much more expensive.
1 parent a645d7f commit ad907d5

File tree

8 files changed

+61
-47
lines changed

8 files changed

+61
-47
lines changed

Make.inc

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,28 @@ WITH_GC_DEBUG_ENV := 0
9696
# Prevent picking up $ARCH from the environment variables
9797
ARCH:=
9898

99+
# We need python for things like BB triplet recognition and relative path computation.
100+
# We don't really care about version, generally, so just find something that works:
101+
PYTHON := "$(shell which python 2>/dev/null || which python3 2>/dev/null || which python2 2>/dev/null || echo not found)"
102+
PYTHON_SYSTEM := $(shell $(PYTHON) -c 'from __future__ import print_function; import platform; print(platform.system())')
103+
104+
# If we're running on Cygwin, but using a native-windows Python, we need to use cygpath -w
105+
ifneq ($(and $(filter $(PYTHON_SYSTEM),Windows),$(findstring CYGWIN,$(shell uname))),)
106+
python_cygpath = `cygpath -w $(1)`
107+
else
108+
python_cygpath = $(1)
109+
endif
110+
111+
# Get a relative path easily
112+
define rel_path
113+
$(shell $(PYTHON) $(call python_cygpath,$(JULIAHOME)/contrib/relative_path.py) $(call python_cygpath,$(1)) $(call python_cygpath,$(2)))
114+
endef
115+
99116
# pick up BUILDROOT from O= if it isn't already set (from recursive make)
100117
ifeq ($(BUILDROOT),)
101118
ifeq ("$(origin O)", "command line")
102119
BUILDROOT := $(abspath $O)
103-
BUILDDIR := $(abspath $(BUILDROOT)/$(shell $(JULIAHOME)/contrib/relative_path.sh $(JULIAHOME) $(SRCDIR)))
120+
BUILDDIR := $(abspath $(BUILDROOT)/$(call rel_path,$(JULIAHOME),$(SRCDIR)))
104121
$(info $(shell printf '\033[32;1mBuilding into $(BUILDROOT)\033[0m')) # use printf to expand the escape sequences
105122
else
106123
BUILDROOT:=$(JULIAHOME)
@@ -277,16 +294,32 @@ private_libdir := $(libdir)/julia
277294
endif
278295
build_private_libdir := $(build_libdir)/julia
279296

297+
# A helper functions for dealing with lazily-evaluated, expensive operations.. Spinning
298+
# up a python process to, for exaxmple, parse a TOML file is expensive, and we must wait
299+
# until the TOML files are on-disk before we can parse them. This means that we cannot
300+
# use `:=` (since we do not want to evaluate these rules now, we want to evaluate them
301+
# when we use them, so we use `=`) however we also do not want to re-evaluate them
302+
# multiple times. So we define a caching mechanism where the rules are still lazily
303+
# evaluated, but we cache the value such that the second time around we don't have to
304+
# re-evaluate them. Usage example:
305+
#
306+
# EXPENSIVE_OPERATION = $(shell prog args...)
307+
# CACHED_RESULT = $(call hit_cache,EXPENSIVE_OPERATION)
308+
#
309+
# The first time you use `$(CACHED_RESULT)`, it will invoke `$(EXPENSIVE_OPERATION)`,
310+
# but after that point, it will not, unless `$(EXPENSIVE_OPERATION)` evaluated to the
311+
# empty string, in which case it will be re-evaluated.
312+
define hit_cache
313+
$(if $(_CACHE-$(1)),,$(eval _CACHE-$(1) := $($(1))))$(_CACHE-$(1))
314+
endef
315+
280316
# Calculate relative paths to libdir, private_libdir, datarootdir, and sysconfdir
281-
build_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(build_bindir) $(build_libdir))
282-
libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(libdir))
283-
build_private_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(build_bindir) $(build_private_libdir))
284-
private_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(private_libdir))
285-
datarootdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(datarootdir))
286-
libexecdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(libexecdir))
287-
docdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(docdir))
288-
sysconfdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(sysconfdir))
289-
includedir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(includedir))
317+
define cache_rel_path
318+
$(1)_rel_eval = $(call rel_path,$(2),$($(1)))
319+
$(1)_rel = $$(call hit_cache,$(1)_rel_eval)
320+
endef
321+
$(foreach D,libdir private_libdir datarootdir libexecdir docdir sysconfdir includedir,$(eval $(call cache_rel_path,$(D),$(bindir))))
322+
$(foreach D,build_libdir build_private_libdir,$(eval $(call cache_rel_path,$(D),$(build_bindir))))
290323

291324
INSTALL_F := $(JULIAHOME)/contrib/install.sh 644
292325
INSTALL_M := $(JULIAHOME)/contrib/install.sh 755

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ $(BUILDROOT)/doc/_build/html/en/index.html: $(shell find $(BUILDROOT)/base $(BUI
4848

4949
julia-symlink: julia-ui-$(JULIA_BUILD_MODE)
5050
ifeq ($(OS),WINNT)
51-
@echo '@"%~dp0"\'"$(shell $(JULIAHOME)/contrib/relative_path.sh "$(BUILDROOT)" "$(JULIA_EXECUTABLE)" | tr / '\\')" '%*' > $(BUILDROOT)/julia.bat
51+
@echo '@"%~dp0"\'"$$(echo $(call rel_path,$(BUILDROOT),$(JULIA_EXECUTABLE)) | tr / '\\')" '%*' > $(BUILDROOT)/julia.bat
5252
chmod a+x $(BUILDROOT)/julia.bat
5353
else
5454
ifndef JULIA_VAGRANT_BUILD
55-
@ln -sf "$(shell $(JULIAHOME)/contrib/relative_path.sh "$(BUILDROOT)" "$(JULIA_EXECUTABLE)")" $(BUILDROOT)/julia
55+
@ln -sf $(call rel_path,$(BUILDROOT),$(JULIA_EXECUTABLE)) $(BUILDROOT)/julia
5656
endif
5757
endif
5858

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ Multi-threading changes
101101

102102
Build system changes
103103
--------------------
104+
* The build system now contains a pure-make caching system for expanding expensive operations at the latest
105+
possible moment, while still expanding it only once. ([#35626])
104106

105107

106108
New library functions

contrib/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Installation
1414
|[ julia-config.jl ](https://github.com/JuliaLang/julia/blob/master/contrib/julia-config.jl) | Determines build parameters required by an embedded Julia |
1515
|[ julia.desktop ](https://github.com/JuliaLang/julia/blob/master/contrib/julia.desktop) | GNOME desktop config file |
1616
|[ mac/ ](https://github.com/JuliaLang/julia/blob/master/contrib/mac/) | Mac install files |
17-
|[ relative_path.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/relative_path.sh) | Convert absolute path into relative path script |
17+
|[ relative_path.py ](https://github.com/JuliaLang/julia/blob/master/contrib/relative_path.py) | Convert absolute paths into relative paths |
1818
|[ repackage_system_suitesparse4.make ](https://github.com/JuliaLang/julia/blob/master/contrib/repackage_system_suitesparse4.make) | Links shared libraries from static-library for suitesparse4 |
1919
|[ stringreplace.c ](https://github.com/JuliaLang/julia/blob/master/contrib/stringreplace.c) | Replace strings to hardcoded paths in binaries during `make install` |
2020
|[ travis_fastfail.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/travis_fastfail.sh ) | Checks for queued build tests in Travis |

contrib/relative_path.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import sys, os
2+
if len(sys.argv) != 3:
3+
sys.stderr.write("\nrelative_path.py - incomplete arguments: %s\n"%(sys.argv))
4+
sys.exit(1)
5+
6+
# We always use `/` as the path separator, no matter what OS we're running on, since our
7+
# shells and whatnot during the build are all POSIX shells/cygwin. We rely on the build
8+
# system itself to canonicalize to `\` when it needs to, and deal with the shell escaping
9+
# and whatnot at the latest possible moment.
10+
sys.stdout.write(os.path.relpath(sys.argv[2], sys.argv[1]).replace(os.path.sep, '/'))

contrib/relative_path.sh

Lines changed: 0 additions & 31 deletions
This file was deleted.

deps/llvm.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_HOST_TRIPL
7979
ifeq ($(USE_POLLY_ACC),1)
8080
LLVM_CMAKE += -DPOLLY_ENABLE_GPGPU_CODEGEN=ON
8181
endif
82-
LLVM_CMAKE += -DLLVM_TOOLS_INSTALL_DIR=$(shell $(JULIAHOME)/contrib/relative_path.sh $(build_prefix) $(build_depsbindir))
83-
LLVM_CMAKE += -DLLVM_UTILS_INSTALL_DIR=$(shell $(JULIAHOME)/contrib/relative_path.sh $(build_prefix) $(build_depsbindir))
82+
LLVM_CMAKE += -DLLVM_TOOLS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir))
83+
LLVM_CMAKE += -DLLVM_UTILS_INSTALL_DIR=$(call rel_path,$(build_prefix),$(build_depsbindir))
8484
LLVM_CMAKE += -DLLVM_INCLUDE_UTILS=ON -DLLVM_INSTALL_UTILS=ON
8585
LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_HISTEDIT_H=Off -DHAVE_LIBEDIT=Off
8686
ifeq ($(LLVM_ASSERTIONS), 1)

sysimage.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ BASE_SRCS := $(sort $(shell find $(JULIAHOME)/base -name \*.jl -and -not -name s
5555
$(shell find $(BUILDROOT)/base -name \*.jl -and -not -name sysimg.jl))
5656
STDLIB_SRCS := $(JULIAHOME)/base/sysimg.jl $(shell find $(build_datarootdir)/julia/stdlib/$(VERSDIR)/*/src -name \*.jl) \
5757
$(build_prefix)/manifest/Pkg
58-
RELBUILDROOT := $(shell $(JULIAHOME)/contrib/relative_path.sh "$(JULIAHOME)/base" "$(BUILDROOT)/base/")
58+
RELBUILDROOT := $(call rel_path,$(JULIAHOME)/base,$(BUILDROOT)/base)/ # <-- make sure this always has a trailing slash
5959

6060
$(build_private_libdir)/corecompiler.ji: $(COMPILER_SRCS)
6161
@$(call PRINT_JULIA, cd $(JULIAHOME)/base && \

0 commit comments

Comments
 (0)