1 # 2 # Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. 3 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 # 5 # This code is free software; you can redistribute it and/or modify it 6 # under the terms of the GNU General Public License version 2 only, as 7 # published by the Free Software Foundation. Oracle designates this 8 # particular file as subject to the "Classpath" exception as provided 9 # by Oracle in the LICENSE file that accompanied this code. 10 # 11 # This code is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 # version 2 for more details (a copy is included in the LICENSE file that 15 # accompanied this code). 16 # 17 # You should have received a copy of the GNU General Public License version 18 # 2 along with this work; if not, write to the Free Software Foundation, 19 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 # 21 # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 # or visit www.oracle.com if you need additional information or have any 23 # questions. 24 # 25 26 include MakeIncludeStart.gmk 27 ifeq ($(INCLUDE), true) 28 29 ################################################################################ 30 # This file contains helper functions for Init.gmk. 31 ################################################################################ 32 33 # Define basic logging setup 34 BUILD_LOG := $(OUTPUTDIR)/build.log 35 BUILD_PROFILE_LOG := $(OUTPUTDIR)/build-profile.log 36 37 BUILD_LOG_PIPE := > >($(TEE) -a $(BUILD_LOG)) 2> >($(TEE) -a $(BUILD_LOG) >&2) && wait 38 # Use this for simple echo/printf commands that are never expected to print 39 # to stderr. 40 BUILD_LOG_PIPE_SIMPLE := | $(TEE) -a $(BUILD_LOG) 41 42 # Setup the build environment to match the requested specification on 43 # level of reproducible builds 44 define SetupReproducibleBuild 45 ifeq ($$(SOURCE_DATE), updated) 46 # For static values of SOURCE_DATE (not "updated"), these are set in spec.gmk 47 export SOURCE_DATE_EPOCH := $$(shell $$(DATE) +"%s") 48 export SOURCE_DATE_ISO_8601 := $$(call EpochToISO8601, $$(SOURCE_DATE_EPOCH)) 49 endif 50 endef 51 52 # Parse COMPARE_BUILD into COMPARE_BUILD_* 53 # Syntax: COMPARE_BUILD=CONF=<configure options>:PATCH=<patch file>: 54 # MAKE=<make targets>:COMP_OPTS=<compare script options>: 55 # COMP_DIR=<compare script base dir>|<default>: 56 # FAIL=<bool> 57 # If neither CONF or PATCH is given, assume <default> means CONF if it 58 # begins with "--", otherwise assume it means PATCH. 59 # MAKE and COMP_OPTS can only be used with CONF and/or PATCH specified. 60 # If any value contains "+", it will be replaced by space. 61 # FAIL can be set to false to have the return value of compare be ignored. 62 define ParseCompareBuild 63 ifneq ($$(COMPARE_BUILD), ) 64 COMPARE_BUILD_OUTPUTDIR := $(WORKSPACE_ROOT)/build/compare-build/$(CONF_NAME) 65 COMPARE_BUILD_FAIL := true 66 67 ifneq ($$(findstring :, $$(COMPARE_BUILD)), ) 68 $$(foreach part, $$(subst :, , $$(COMPARE_BUILD)), \ 69 $$(if $$(filter PATCH=%, $$(part)), \ 70 $$(eval COMPARE_BUILD_PATCH = $$(strip $$(patsubst PATCH=%, %, $$(part)))) \ 71 ) \ 72 $$(if $$(filter CONF=%, $$(part)), \ 73 $$(eval COMPARE_BUILD_CONF = $$(strip $$(subst +, , $$(patsubst CONF=%, %, $$(part))))) \ 74 ) \ 75 $$(if $$(filter MAKE=%, $$(part)), \ 76 $$(eval COMPARE_BUILD_MAKE = $$(strip $$(subst +, , $$(patsubst MAKE=%, %, $$(part))))) \ 77 ) \ 78 $$(if $$(filter COMP_OPTS=%, $$(part)), \ 79 $$(eval COMPARE_BUILD_COMP_OPTS = $$(strip $$(subst +, , $$(patsubst COMP_OPTS=%, %, $$(part))))) \ 80 ) \ 81 $$(if $$(filter COMP_DIR=%, $$(part)), \ 82 $$(eval COMPARE_BUILD_COMP_DIR = $$(strip $$(subst +, , $$(patsubst COMP_DIR=%, %, $$(part))))) \ 83 ) \ 84 $$(if $$(filter FAIL=%, $$(part)), \ 85 $$(eval COMPARE_BUILD_FAIL = $$(strip $$(subst +, , $$(patsubst FAIL=%, %, $$(part))))) \ 86 ) \ 87 $$(if $$(filter NODRYRUN=%, $$(part)), \ 88 $$(eval COMPARE_BUILD_NODRYRUN = $$(strip $$(subst +, , $$(patsubst NODRYRUN=%, %, $$(part))))) \ 89 ) \ 90 ) 91 else 92 # Separate handling for single field case, to allow for spaces in values. 93 ifneq ($$(filter PATCH=%, $$(COMPARE_BUILD)), ) 94 COMPARE_BUILD_PATCH = $$(strip $$(patsubst PATCH=%, %, $$(COMPARE_BUILD))) 95 else ifneq ($$(filter CONF=%, $$(COMPARE_BUILD)), ) 96 COMPARE_BUILD_CONF = $$(strip $$(subst +, , $$(patsubst CONF=%, %, $$(COMPARE_BUILD)))) 97 else ifneq ($$(filter --%, $$(COMPARE_BUILD)), ) 98 # Assume CONF if value begins with -- 99 COMPARE_BUILD_CONF = $$(strip $$(subst +, , $$(COMPARE_BUILD))) 100 else 101 # Otherwise assume patch file 102 COMPARE_BUILD_PATCH = $$(strip $$(COMPARE_BUILD)) 103 endif 104 endif 105 ifneq ($$(COMPARE_BUILD_PATCH), ) 106 ifneq ($$(wildcard $$(WORKSPACE_ROOT)/$$(COMPARE_BUILD_PATCH)), ) 107 # Assume relative path, if file exists 108 COMPARE_BUILD_PATCH := $$(wildcard $$(WORKSPACE_ROOT)/$$(COMPARE_BUILD_PATCH)) 109 else ifeq ($$(wildcard $$(COMPARE_BUILD_PATCH)), ) 110 $$(error Patch file $$(COMPARE_BUILD_PATCH) does not exist) 111 endif 112 ifneq ($$(COMPARE_BUILD_NODRYRUN), true) 113 PATCH_DRY_RUN := $$(shell cd $$(WORKSPACE_ROOT) && $$(PATCH) --dry-run -p1 < $$(COMPARE_BUILD_PATCH) > /dev/null 2>&1 || $$(ECHO) FAILED) 114 ifeq ($$(PATCH_DRY_RUN), FAILED) 115 $$(error Patch file $$(COMPARE_BUILD_PATCH) does not apply cleanly) 116 endif 117 endif 118 endif 119 ifneq ($$(COMPARE_BUILD_FAIL), true) 120 COMPARE_BUILD_IGNORE_RESULT := || true 121 endif 122 endif 123 endef 124 125 # Prepare for a comparison rebuild 126 define PrepareCompareBuild 127 $(ECHO) "Preparing for comparison rebuild" 128 # Apply patch, if any 129 $(if $(COMPARE_BUILD_PATCH), cd $(WORKSPACE_ROOT) && $(PATCH) -p1 < $(COMPARE_BUILD_PATCH)) 130 # Move the first build away temporarily 131 $(RM) -r $(WORKSPACE_ROOT)/build/.compare-build-temp 132 $(MKDIR) -p $(WORKSPACE_ROOT)/build/.compare-build-temp 133 $(MV) $(OUTPUTDIR) $(WORKSPACE_ROOT)/build/.compare-build-temp 134 # Restore an old compare-build, or create a new compare-build directory. 135 if test -d $(COMPARE_BUILD_OUTPUTDIR); then \ 136 $(MV) $(COMPARE_BUILD_OUTPUTDIR) $(OUTPUTDIR); \ 137 else \ 138 $(MKDIR) -p $(OUTPUTDIR); \ 139 fi 140 # Re-run configure with the same arguments (and possibly some additional), 141 # must be done after patching. 142 ( cd $(CONFIGURE_START_DIR) && PATH="$(ORIGINAL_PATH)" \ 143 $(BASH) $(WORKSPACE_ROOT)/configure $(CONFIGURE_COMMAND_LINE) $(COMPARE_BUILD_CONF)) 144 endef 145 146 # Cleanup after a compare build 147 define CleanupCompareBuild 148 # If running with a COMPARE_BUILD patch, reverse-apply it, but continue 149 # even if that fails (can happen with removed files). 150 $(if $(COMPARE_BUILD_PATCH), cd $(WORKSPACE_ROOT) && $(PATCH) -R -p1 < $(COMPARE_BUILD_PATCH) || true) 151 # Move this build away and restore the original build 152 $(MKDIR) -p $(WORKSPACE_ROOT)/build/compare-build 153 $(MV) $(OUTPUTDIR) $(COMPARE_BUILD_OUTPUTDIR) 154 $(MV) $(WORKSPACE_ROOT)/build/.compare-build-temp/$(CONF_NAME) $(OUTPUTDIR) 155 $(RM) -r $(WORKSPACE_ROOT)/build/.compare-build-temp 156 endef 157 158 # Do the actual comparison of two builds 159 define CompareBuildDoComparison 160 # Compare first and second build. Ignore any error code from compare.sh. 161 $(ECHO) "Comparing between comparison rebuild (this/new) and baseline (other/old)" 162 $(if $(COMPARE_BUILD_COMP_DIR), \ 163 +(cd $(COMPARE_BUILD_OUTPUTDIR) && ./compare.sh --diffs $(COMPARE_BUILD_COMP_OPTS) \ 164 -2dirs $(COMPARE_BUILD_OUTPUTDIR)/$(COMPARE_BUILD_COMP_DIR) \ 165 $(OUTPUTDIR)/$(COMPARE_BUILD_COMP_DIR) $(COMPARE_BUILD_IGNORE_RESULT)), \ 166 +(cd $(COMPARE_BUILD_OUTPUTDIR) && ./compare.sh --diffs $(COMPARE_BUILD_COMP_OPTS) \ 167 -o $(OUTPUTDIR) $(COMPARE_BUILD_IGNORE_RESULT)) \ 168 ) 169 endef 170 171 define PrintFailureReports 172 $(if $(filter none, $(LOG_REPORT)), , \ 173 $(RM) $(MAKESUPPORT_OUTPUTDIR)/failure-summary.log ; \ 174 $(if $(wildcard $(MAKESUPPORT_OUTPUTDIR)/failure-logs/*.log), \ 175 ( \ 176 $(PRINTF) "\n=== Output from failing command(s) repeated here ===\n" ; \ 177 $(foreach logfile, $(sort $(wildcard $(MAKESUPPORT_OUTPUTDIR)/failure-logs/*.log)), \ 178 $(PRINTF) "* For target $(notdir $(basename $(logfile))):\n" ; \ 179 $(if $(filter all, $(LOG_REPORT)), \ 180 $(GREP) -v -e "^Note: including file:" < $(logfile) || true ; \ 181 , \ 182 ($(GREP) -v -e "^Note: including file:" < $(logfile) || true) | $(HEAD) -n 15 ; \ 183 if test `$(WC) -l < $(logfile)` -gt 15; then \ 184 $(ECHO) " ... (rest of output omitted)" ; \ 185 fi ; \ 186 ) \ 187 ) \ 188 $(PRINTF) "\n* All command lines available in $(MAKESUPPORT_OUTPUTDIR)/failure-logs.\n" ; \ 189 $(PRINTF) "=== End of repeated output ===\n" ; \ 190 ) >> $(MAKESUPPORT_OUTPUTDIR)/failure-summary.log \ 191 ) \ 192 ) 193 endef 194 195 define PrintBuildLogFailures 196 $(if $(filter none, $(LOG_REPORT)), , \ 197 if $(GREP) -q "recipe for target .* failed" $(BUILD_LOG) 2> /dev/null; then \ 198 $(PRINTF) "\n=== Make failed targets repeated here ===\n" ; \ 199 $(GREP) "recipe for target .* failed" $(BUILD_LOG) ; \ 200 $(PRINTF) "=== End of repeated output ===\n" ; \ 201 $(PRINTF) "\nHELP: Try searching the build log for the name of the first failed target.\n" ; \ 202 else \ 203 $(PRINTF) "\nNo indication of failed target found.\n" ; \ 204 $(PRINTF) "HELP: Try searching the build log for '] Error'.\n" ; \ 205 fi >> $(MAKESUPPORT_OUTPUTDIR)/failure-summary.log ; \ 206 $(CAT) $(MAKESUPPORT_OUTPUTDIR)/failure-summary.log \ 207 ) 208 endef 209 210 define RotateLogFiles 211 $(RM) $(BUILD_LOG).old 2> /dev/null && \ 212 $(MV) $(BUILD_LOG) $(BUILD_LOG).old 2> /dev/null || true 213 $(if $(findstring true, $(LOG_PROFILE_TIMES_FILE)), \ 214 $(RM) $(BUILD_PROFILE_LOG).old 2> /dev/null && \ 215 $(MV) $(BUILD_PROFILE_LOG) $(BUILD_PROFILE_LOG).old 2> /dev/null || true \ 216 ) 217 endef 218 219 # Failure logs are only supported for "parallel" main targets, not the 220 # (trivial) sequential make targets (such as clean and reconfigure), 221 # since the failure-logs directory creation will conflict with clean. 222 # We also make sure the javatmp directory exists, which is needed if a java 223 # process (like javac) is using java.io.tmpdir. 224 define PrepareFailureLogs 225 $(RM) -r $(MAKESUPPORT_OUTPUTDIR)/failure-logs 2> /dev/null && \ 226 $(MKDIR) -p $(MAKESUPPORT_OUTPUTDIR)/failure-logs 227 $(MKDIR) -p $(JAVA_TMP_DIR) 228 $(RM) $(MAKESUPPORT_OUTPUTDIR)/exit-with-error 2> /dev/null 229 endef 230 231 # Remove any javac server logs and port files. This 232 # prevents a new make run to reuse the previous servers. 233 define PrepareJavacServer 234 $(if $(JAVAC_SERVER_DIR), \ 235 $(RM) -r $(JAVAC_SERVER_DIR) 2> /dev/null && \ 236 $(MKDIR) -p $(JAVAC_SERVER_DIR) \ 237 ) 238 endef 239 240 define CleanupJavacServer 241 [ -f $(JAVAC_SERVER_DIR)/server.port ] && $(ECHO) Stopping javac server && \ 242 $(TOUCH) $(JAVAC_SERVER_DIR)/server.port.stop; true 243 endef 244 245 ifeq ($(call isBuildOs, windows), true) 246 # On windows we need to synchronize with the javac server to be able to 247 # move or remove the build output directory. Since we have no proper 248 # synchronization process, wait for a while and hope it helps. This is only 249 # used by build comparisons. 250 define WaitForJavacServerFinish 251 $(if $(JAVAC_SERVER_DIR), \ 252 sleep 5 \ 253 ) 254 endef 255 else 256 define WaitForJavacServerFinish 257 endef 258 endif 259 260 ############################################################################## 261 # Functions for timers 262 ############################################################################## 263 264 # Store the build times in this directory. 265 BUILDTIMESDIR := $(OUTPUTDIR)/make-support/build-times 266 267 # Record starting time for build of a sub repository. 268 define RecordStartTime 269 $(DATE) '+%Y %m %d %H %M %S' | $(AWK) '{ print $$1,$$2,$$3,$$4,$$5,$$6,($$4*3600+$$5*60+$$6) }' > $(BUILDTIMESDIR)/build_time_start_$(strip $1) && \ 270 $(DATE) '+%Y-%m-%d %H:%M:%S' > $(BUILDTIMESDIR)/build_time_start_$(strip $1)_human_readable 271 endef 272 273 # Record ending time and calculate the difference and store it in a 274 # easy to read format. Handles builds that cross midnight. Expects 275 # that a build will never take 24 hours or more. 276 define RecordEndTime 277 $(DATE) '+%Y %m %d %H %M %S' | $(AWK) '{ print $$1,$$2,$$3,$$4,$$5,$$6,($$4*3600+$$5*60+$$6) }' > $(BUILDTIMESDIR)/build_time_end_$(strip $1) 278 $(DATE) '+%Y-%m-%d %H:%M:%S' > $(BUILDTIMESDIR)/build_time_end_$(strip $1)_human_readable 279 $(ECHO) `$(CAT) $(BUILDTIMESDIR)/build_time_start_$(strip $1)` `$(CAT) $(BUILDTIMESDIR)/build_time_end_$(strip $1)` $1 | \ 280 $(AWK) '{ F=$$7; T=$$14; if (F > T) { T+=3600*24 }; D=T-F; H=int(D/3600); \ 281 M=int((D-H*3600)/60); S=D-H*3600-M*60; printf("%02d:%02d:%02d %s\n",H,M,S,$$15); }' \ 282 > $(BUILDTIMESDIR)/build_time_diff_$(strip $1) 283 endef 284 285 define StartGlobalTimer 286 $(RM) -r $(BUILDTIMESDIR) 2> /dev/null && \ 287 $(MKDIR) -p $(BUILDTIMESDIR) && \ 288 $(call RecordStartTime,TOTAL) 289 endef 290 291 define StopGlobalTimer 292 $(call RecordEndTime,TOTAL) 293 endef 294 295 # Find all build_time_* files and print their contents in a list sorted 296 # on the name of the sub repository. 297 define ReportBuildTimes 298 $(PRINTF) $(LOG_INFO) -- \ 299 "----- Build times -------\nStart %s\nEnd %s\n%s\n%s\n-------------------------\n" \ 300 "`$(CAT) $(BUILDTIMESDIR)/build_time_start_TOTAL_human_readable`" \ 301 "`$(CAT) $(BUILDTIMESDIR)/build_time_end_TOTAL_human_readable`" \ 302 "`$(LS) $(BUILDTIMESDIR)/build_time_diff_* | $(GREP) -v _TOTAL | \ 303 $(XARGS) $(CAT) | $(SORT) -k 2`" \ 304 "`$(CAT) $(BUILDTIMESDIR)/build_time_diff_TOTAL`" \ 305 $(BUILD_LOG_PIPE_SIMPLE) 306 endef 307 308 define ReportProfileTimes 309 $(if $(findstring true, $(LOG_PROFILE_TIMES_LOG)), \ 310 [ ! -f $(BUILD_PROFILE_LOG) ] || \ 311 { $(ECHO) Begin $(notdir $(BUILD_PROFILE_LOG)) && \ 312 $(CAT) $(BUILD_PROFILE_LOG) && \ 313 $(ECHO) End $(notdir $(BUILD_PROFILE_LOG)); \ 314 } \ 315 $(BUILD_LOG_PIPE_SIMPLE) 316 ) 317 endef 318 319 ################################################################################ 320 321 endif # include guard 322 include MakeIncludeEnd.gmk