# # Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License version 2 only, as # published by the Free Software Foundation. Oracle designates this # particular file as subject to the "Classpath" exception as provided # by Oracle in the LICENSE file that accompanied this code. # # This code is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # version 2 for more details (a copy is included in the LICENSE file that # accompanied this code). # # You should have received a copy of the GNU General Public License version # 2 along with this work; if not, write to the Free Software Foundation, # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. # # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA # or visit www.oracle.com if you need additional information or have any # questions. # ifeq ($(_MAKEBASE_GMK), ) $(error You must include MakeBase.gmk prior to including FileUtils.gmk) endif ################################################################################ # # Common file utility functions # ################################################################################ ################################################################################ # Replace question marks with space in string. This macro needs to be called on # files from FindFiles in case any of them contains space in their file name, # since FindFiles replaces space with ?. # Param 1 - String to replace in DecodeSpace = \ $(subst ?,$(SPACE),$(strip $1)) EncodeSpace = \ $(subst $(SPACE),?,$(strip $1)) ################################################################################ # Take two paths and return the path of the last common directory. # Ex: /foo/bar/baz, /foo/bar/banan -> /foo/bar # foo/bar/baz, /foo/bar -> # # The x prefix is used to preserve the presence of the initial slash # On Windows paths are treated as case-insensitive # # $1 - Path to compare # $2 - Other path to compare FindCommonPathPrefix = \ $(call DecodeSpace,$(patsubst x%,%,$(subst $(SPACE),/,$(strip \ $(call FindCommonPathPrefixHelper1, \ $(subst /,$(SPACE),x$(call EncodeSpace,$(strip $1))), \ $(subst /,$(SPACE),x$(call EncodeSpace,$(strip $2)))) \ )))) FindCommonPathPrefixHelper1 = \ $(if $(filter $(OPENJDK_TARGET_OS), windows), \ $(call FindCommonPathPrefixHelper2,$(call uppercase,$1),$(call uppercase,$2),$1), \ $(call FindCommonPathPrefixHelper2,$1,$2,$1)) FindCommonPathPrefixHelper2 = \ $(if $(call equals, $(firstword $1), $(firstword $2)), \ $(if $(call equals, $(firstword $1),),, \ $(firstword $3) \ $(call FindCommonPathPrefixHelper2, \ $(wordlist 2, $(words $1), $1), \ $(wordlist 2, $(words $2), $2), \ $(wordlist 2, $(words $3), $3) \ ) \ ) \ ) # Computes the relative path from a directory to a file # $1 - File to compute the relative path to # $2 - Directory to compute the relative path from RelativePath = \ $(call DecodeSpace,$(strip $(call RelativePathHelper,$(call EncodeSpace \ ,$(strip $1)),$(call EncodeSpace \ ,$(strip $2)),$(call EncodeSpace \ ,$(call FindCommonPathPrefix,$1,$2))))) RelativePathHelper = \ $(eval $3_prefix_length := $(words $(subst /,$(SPACE),$3))) \ $(eval $1_words := $(subst /,$(SPACE),$1)) \ $(eval $2_words := $(subst /,$(SPACE),$2)) \ $(if $(call equals,$($3_prefix_length),0),, \ $(eval $1_words := $(wordlist 2,$(words $($1_words)),$(wordlist \ $($3_prefix_length),$(words $($1_words)),$($1_words)))) \ $(eval $2_words := $(wordlist 2,$(words $($2_words)),$(wordlist \ $($3_prefix_length),$(words $($2_words)),$($2_words)))) \ ) \ $(eval $1_suffix := $(subst $(SPACE),/,$($1_words))) \ $(eval $2_dotdots := $(subst $(SPACE),/,$(foreach d,$($2_words),..))) \ $(if $($1_suffix), \ $(if $($2_dotdots), $($2_dotdots)/$($1_suffix), $($1_suffix)), \ $(if $($2_dotdots), $($2_dotdots), .)) # Make directory for target file. Should handle spaces in filenames. Just # calling $(call MakeDir $(@D)) will not work if the directory contains a space # and the target file already exists. In that case, the target file will have # its wildcard ? resolved and the $(@D) will evaluate each space separated dir # part on its own. MakeTargetDir = \ $(call MakeDir, $(dir $(call EncodeSpace, $@))) ################################################################################ # All install-file and related macros automatically call DecodeSpace when needed. ifeq ($(call isTargetOs, macosx), true) # On mac, extended attributes sometimes creep into the source files, which may later # cause the creation of ._* files which confuses testing. Clear these with xattr if # set. Some files get their write permissions removed after being copied to the # output dir. When these are copied again to images, xattr would fail. By only clearing # attributes when they are present, failing on this is avoided. # # If copying a soft link to a directory, need to delete the target first to avoid # weird errors. define install-file $(call MakeTargetDir) $(RM) '$(call DecodeSpace, $@)' # Work around a weirdness with cp on Macosx. When copying a symlink, if # the target of the link is write protected (e.g. 444), cp will add # write permission for the user on the target file (644). Avoid this by # using ln to create a new link instead. if [ -h '$(call DecodeSpace, $<)' ]; then \ $(LN) -s "`$(READLINK) '$(call DecodeSpace, $<)'`" '$(call DecodeSpace, $@)'; \ else \ $(CP) -fRP '$(call DecodeSpace, $<)' '$(call DecodeSpace, $@)'; \ fi if [ -n "`$(XATTR) -ls '$(call DecodeSpace, $@)'`" ]; then \ $(CHMOD) -h u+w '$(call DecodeSpace, $@)'; \ $(XATTR) -cs '$(call DecodeSpace, $@)'; \ fi endef else define install-file $(call MakeTargetDir) $(CP) -fP '$(call DecodeSpace, $<)' '$(call DecodeSpace, $@)' endef endif # Variant of install file that does not preserve symlinks define install-file-nolink $(call MakeTargetDir) $(CP) -f '$(call DecodeSpace, $<)' '$(call DecodeSpace, $@)' endef ################################################################################ # link-file-* works similarly to install-file but creates a symlink instead. # There are two versions, either creating a relative or an absolute link. Be # careful when using this on Windows since the symlink created is only valid in # the unix emulation environment. # In msys2 we use mklink /J because its ln would perform a deep copy of the target. # This inhibits performance and can lead to issues with long paths. With mklink /J # relative linking does not work, so we handle the link as absolute path. ifeq ($(OPENJDK_BUILD_OS_ENV), windows.msys2) define link-file-relative $(call MakeTargetDir) $(RM) '$(call DecodeSpace, $@)' cmd //c "mklink /J $(call FixPath, $(call DecodeSpace, $@)) $(call FixPath, $(call DecodeSpace, $<))" endef else define link-file-relative $(call MakeTargetDir) $(RM) '$(call DecodeSpace, $@)' $(LN) -s '$(call DecodeSpace, $(call RelativePath, $<, $(@D)))' '$(call DecodeSpace, $@)' endef endif ifeq ($(OPENJDK_BUILD_OS_ENV), windows.msys2) define link-file-absolute $(call MakeTargetDir) $(RM) '$(call DecodeSpace, $@)' cmd //c "mklink /J $(call FixPath, $(call DecodeSpace, $@)) $(call FixPath, $(call DecodeSpace, $<))" endef else define link-file-absolute $(call MakeTargetDir) $(RM) '$(call DecodeSpace, $@)' $(LN) -s '$(call DecodeSpace, $<)' '$(call DecodeSpace, $@)' endef endif define copy-and-chmod-executable $(install-file) $(CHMOD) a+rx $@ endef ################################################################################ # Recursive wildcard function. Walks down directories recursively and matches # files with the search patterns. Patterns use standard file wildcards (* and # ?). # # $1 - Directories to start search in # $2 - Search patterns rwildcard = \ $(strip \ $(foreach d, \ $(patsubst %/,%,$(sort $(dir $(wildcard $(addsuffix /*/*, $(strip $1)))))), \ $(call rwildcard,$d,$2) \ ) \ $(call DoubleDollar, $(wildcard $(foreach p, $2, $(addsuffix /$(strip $p), $(strip $1))))) \ ) # Find non directories using recursive wildcard function. This function may # be used directly when a small amount of directories is expected to be # searched and caching is not expected to be of use. # # $1 - Directory to start search in # $2 - Optional search patterns, defaults to '*'. WildcardFindFiles = \ $(sort $(strip \ $(eval WildcardFindFiles_result := $(call rwildcard,$(patsubst %/,%,$1),$(if $(strip $2),$2,*))) \ $(filter-out $(patsubst %/,%,$(sort $(dir $(WildcardFindFiles_result)))), \ $(WildcardFindFiles_result) \ ) \ )) # Find non directories using the find utility in the shell. Safe to call for # non existing directories, or directories containing wildcards. # # Files containing space will get spaces replaced with ? because GNU Make # cannot handle lists of files with space in them. By using ?, make will match # the wildcard to space in many situations so we don't need to replace back # to space on every use. While not a complete solution it does allow some uses # of FindFiles to function with spaces in file names, including for # SetupCopyFiles. Unfortunately this does not work for WildcardFindFiles so # if files with spaces are anticipated, use ShellFindFiles directly. # # $1 - Directories to start search in. # $2 - Optional search patterns, empty means find everything. Patterns use # standard file wildcards (* and ?) and should not be quoted. # $3 - Optional options to find. ShellFindFiles = \ $(if $(wildcard $1), \ $(sort \ $(shell $(FIND) $3 $(patsubst %/,%,$(wildcard $1)) \( -type f -o -type l \) \ $(if $(strip $2), -a \( -name "$(firstword $2)" \ $(foreach p, $(filter-out $(firstword $2), $2), -o -name "$(p)") \)) \ | $(TR) ' ' '?' \ ) \ ) \ ) # Find non directories using the method most likely to work best for the # current build host # # $1 - Directory to start search in # $2 - Optional search patterns, defaults to '*'. ifeq ($(OPENJDK_BUILD_OS)-$(RWILDCARD_WORKS), windows-true) DirectFindFiles = $(WildcardFindFiles) else DirectFindFiles = $(ShellFindFiles) endif # Finds files using a cache that is populated by FillFindCache below. If any of # the directories given have not been cached, DirectFindFiles is used for # everything. Caching is especially useful in Cygwin, where file finds are very # costly. # # $1 - Directories to start search in. # $2 - Optional search patterns. If used, no caching is done. CacheFindFiles_CACHED_DIRS := CacheFindFiles_CACHED_FILES := CacheFindFiles = \ $(if $2, \ $(call DirectFindFiles, $1, $2) \ , \ $(if $(filter-out $(addsuffix /%, $(CacheFindFiles_CACHED_DIRS)) \ $(CacheFindFiles_CACHED_DIRS), $1), \ $(call DirectFindFiles, $1) \ , \ $(filter $(addsuffix /%,$(patsubst %/,%,$1)) $1,$(CacheFindFiles_CACHED_FILES)) \ ) \ ) # Explicitly adds files to the find cache used by CacheFindFiles. # # $1 - Directories to start search in FillFindCache = \ $(eval CacheFindFiles_NEW_DIRS := $$(filter-out $$(addsuffix /%, \ $$(CacheFindFiles_CACHED_DIRS)) $$(CacheFindFiles_CACHED_DIRS), $1)) \ $(if $(CacheFindFiles_NEW_DIRS), \ $(eval CacheFindFiles_CACHED_DIRS += $$(patsubst %/,%,$$(CacheFindFiles_NEW_DIRS))) \ $(eval CacheFindFiles_CACHED_FILES := $$(sort $$(CacheFindFiles_CACHED_FILES) \ $$(call DirectFindFiles, $$(CacheFindFiles_NEW_DIRS)))) \ ) # Findfiles is the default macro that should be used to find files in the file # system. This function does not always support files with spaces in the names. # If files with spaces are anticipated, use ShellFindFiles directly. # # $1 - Directories to start search in. # $2 - Optional search patterns, empty means find everything. Patterns use # standard file wildcards (* and ?) and should not be quoted. ifeq ($(DISABLE_CACHE_FIND), true) FindFiles = $(DirectFindFiles) else FindFiles = $(CacheFindFiles) endif