1 # 2 # Copyright (c) 2013, 2019, 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 ifeq (,$(_MAKEBASE_GMK)) 27 $(error You must include MakeBase.gmk prior to including TextFileProcessing.gmk) 28 endif 29 30 # Helper function for SetupTextFileProcessing; adds a rule for a single file 31 # to be processed. 32 # param 1 = The namespace argument, e.g. BUILD_VERSION_FILE 33 # param 2 = the source file name (full path) 34 # param 3 = the target base directory 35 # param 4 = the target file name (possibly with a partial path) 36 define SetupSingleTextFileForProcessing 37 $(strip $3)/$(strip $4): $2 $$($1_VARDEPS_FILE) 38 $$(call LogInfo, Processing $(strip $4)) 39 $$(call MakeDir, $$(@D)) 40 $(RM) '$$@' '$$@.includes.tmp' '$$@.replacements.tmp' ; \ 41 $$($1_INCLUDES_COMMAND_LINE) < '$$<' > '$$@.includes.tmp' ; \ 42 $$($1_REPLACEMENTS_COMMAND_LINE) < '$$@.includes.tmp' > '$$@.replacements.tmp' ; \ 43 $(RM) '$$@.includes.tmp' ; \ 44 $(MV) '$$@.replacements.tmp' '$$@' 45 46 $1 += $(strip $3)/$(strip $4) 47 endef 48 49 # Setup make rules for processing one or more text files, in which specified 50 # markers are replaced with a given text, or with the contents of a given file. 51 # 52 # Parameter 1 is the name of the rule. This name is used as variable prefix, 53 # and the targets generated are listed in a variable by that name. 54 # 55 # Remaining parameters are named arguments. These include: 56 # SOURCE_DIRS one or more directory roots to search for files to process 57 # SOURCE_FILES complete paths to one or more files to process 58 # OUTPUT_DIR the directory where we store the processed files. 59 # OUTPUT_FILE the name of the resulting file. Only allowed if processing a 60 # single file. 61 # SOURCE_BASE_DIR a common root to all SOURCE_DIRS. 62 # If specified, files will keep the path relative to the base in the 63 # OUTPUT_DIR. Otherwise, the hierarchy will be flattened into the OUTPUT_DIR. 64 # INCLUDE_FILES only include files matching these patterns (used only with 65 # SOURCE_DIRS) 66 # EXCLUDE_FILES exclude files matching these patterns (used only with 67 # SOURCE_DIRS) 68 # INCLUDES replace the occurances of a pattern with the contents of a file; 69 # one or more such include pattern, using the syntax: 70 # PLACEHOLDER => FILE_TO_INCLUDE ; ... 71 # Each PLACEHOLDER must be on a single, otherwise empty line (whitespace 72 # padding is allowed). 73 # REPLACEMENTS one or more text replacement patterns, using the syntax: 74 # PATTERN => REPLACEMENT_TEXT ; ... 75 # 76 # If both INCLUDES or REPLACEMENTS are present, then the includes will be 77 # processed first, and replacements will be done on the included fragments as well. 78 # If neither is present, the files will just be copied without modifications. 79 # 80 SetupTextFileProcessing = $(NamedParamsMacroTemplate) 81 define SetupTextFileProcessingBody 82 83 ifneq ($$($1_SOURCE_FILES),) 84 ifneq ($$($1_SOURCE_DIRS),) 85 $$(error Cannot use both SOURCE_FILES and SOURCE_DIRS (in $1)) 86 endif 87 ifneq ($$($1_SOURCE_BASE_DIR),) 88 $$(error Cannot use SOURCE_BASE_DIR without SOURCE_DIRS (in $1)) 89 endif 90 ifneq ($$($1_EXCLUDE_FILES)$$($1_INCLUDE_FILES),) 91 $$(error Cannot INCLUDE/EXCLUDE_FILES with SOURCE_FILES (in $1)) 92 endif 93 else 94 ifeq ($$($1_SOURCE_DIRS),) 95 $$(error Must specify either SOURCE_FILES or SOURCE_DIRS (in $1)) 96 endif 97 # Find all files in the source trees. Sort to remove duplicates. 98 $$(foreach src, $$($1_SOURCE_DIRS), $$(if $$(wildcard $$(src)), , \ 99 $$(error SOURCE_DIRS contains missing directory $$(src) (in $1)))) 100 ifneq ($$($1_SOURCE_BASE_DIR),) 101 $$(foreach src, $$($1_SOURCE_DIRS), \ 102 $$(if $$(findstring $$($1_SOURCE_BASE_DIR), $$(src)), , \ 103 $$(error SOURCE_DIRS contains directory $$(src) outside \ 104 SOURCE_BASE_DIR $$($1_SOURCE_BASE_DIR) (in $1)))) 105 endif 106 $1_SOURCE_FILES := $$(sort $$(call FindFiles,$$($1_SOURCE_DIRS))) 107 $1_EXCLUDE_FILES:=$$(foreach i,$$($1_SOURCE_DIRS),$$(addprefix $$i/,$$($1_EXCLUDE_FILES))) 108 $1_INCLUDE_FILES:=$$(foreach i,$$($1_SOURCE_DIRS),$$(addprefix $$i/,$$($1_INCLUDE_FILES))) 109 $1_SOURCE_FILES := $$(filter-out $$($1_EXCLUDE_FILES),$$($1_SOURCE_FILES)) 110 ifneq (,$$(strip $$($1_INCLUDE_FILES))) 111 $1_SOURCE_FILES := $$(filter $$($1_INCLUDE_FILES),$$($1_SOURCE_FILES)) 112 endif 113 ifeq (,$$($1_SOURCE_FILES)) 114 $$(info No sources found for $1 when looking inside the dirs $$($1_SRC)) 115 endif 116 endif 117 118 ifneq ($$($1_REPLACEMENTS),) 119 # We have a replacement request, prepare it for the recipe 120 ifneq ($$(findstring /,$$($1_REPLACEMENTS)),) 121 # Cannot use / as separator 122 ifneq ($$(findstring @,$$($1_REPLACEMENTS)),) 123 # Cannot use @ as separator 124 ifneq ($$(findstring |,$$($1_REPLACEMENTS)),) 125 # Cannot use | as separator 126 ifneq ($$(findstring !,$$($1_REPLACEMENTS)),) 127 # Cannot use ! as separator. Give up. 128 $$(error No suitable sed separator can be found for $1. Tested /, @, | and !) 129 else 130 $1_SEP := ! 131 endif 132 else 133 $1_SEP := | 134 endif 135 else 136 $1_SEP := @ 137 endif 138 else 139 $1_SEP := / 140 endif 141 142 # If we have a trailing "=>" (i.e. last rule replaces with empty, and is not 143 # terminated by a ;), add a trailing ; to minimize the number of corner 144 # cases in the hairy subst expression.. 145 ifeq ($$(lastword $$($1_REPLACEMENTS)), =>) 146 $1_REPLACEMENTS += ; 147 endif 148 149 # If we have a trailing ";", add a dummy replacement, since there is no easy 150 # way to delete the last word in make. 151 ifeq ($$(lastword $$($1_REPLACEMENTS)), ;) 152 $1_REPLACEMENTS += DUMMY_REPLACEMENT => DUMMY_REPLACEMENT 153 endif 154 155 # Convert the REPLACEMENTS syntax ( A => B ; C => D ; ...) to a sed command 156 # line (-e "s/A/B/g" -e "s/C/D/g" ...), basically by replacing '=>' with '/' 157 # and ';' with '/g" -e "s/', and adjusting for edge cases. 158 $1_REPLACEMENTS_COMMAND_LINE := $(SED) -e 's$$($1_SEP)$$(subst $$(SPACE);$$(SPACE),$$($1_SEP)g' \ 159 -e 's$$($1_SEP),$$(subst $$(SPACE)=>$$(SPACE),$$($1_SEP),$$(subst $$(SPACE)=>$$(SPACE);$$(SPACE),$$($1_SEP)$$($1_SEP)g' \ 160 -e 's$$($1_SEP),$$(strip $$($1_REPLACEMENTS)))))$$($1_SEP)g' 161 else 162 # We don't have any replacements, just pipe the file through cat. 163 $1_REPLACEMENTS_COMMAND_LINE := $(CAT) 164 endif 165 166 ifneq ($$($1_INCLUDES),) 167 # We have a include request, prepare it for the recipe. 168 # Convert an INCLUDE like this PATTERN_1 => file1 ; PATTERN_2 => file2 ; 169 # into an awk script fragment like this: 170 # { 171 # if (matches("PATTERN_1")) { include("file1") } else 172 # if (matches("PATTERN_2")) { include("file2") } else 173 # print 174 # } 175 176 $1_INCLUDES_HEADER_AWK := \ 177 function matches(pattern) { return ($$$$0 ~ "^[ \t]*" pattern "[ \t]*$$$$") } \ 178 function include(filename) { while ((getline < filename) == 1) print ; close(filename) } 179 $1_INCLUDES_PARTIAL_AWK := $$(subst $$(SPACE);,,$$(subst $$(SPACE)=>$$(SPACE),"$$(RIGHT_PAREN)$$(RIGHT_PAREN) \ 180 { include$$(LEFT_PAREN)",$$(subst $$(SPACE);$$(SPACE),"$$(RIGHT_PAREN) } \ 181 else if $$(LEFT_PAREN)matches$$(LEFT_PAREN)",$$(strip $$($1_INCLUDES))))) 182 $1_INCLUDES_COMMAND_LINE := $(NAWK) '$$($1_INCLUDES_HEADER_AWK) \ 183 { if (matches("$$($1_INCLUDES_PARTIAL_AWK)") } else print }' 184 else 185 # We don't have any includes, just pipe the file through cat. 186 $1_INCLUDES_COMMAND_LINE := $(CAT) 187 endif 188 189 $1_VARDEPS := $$($1_INCLUDES_COMMAND_LINE) $$($1_REPLACEMENTS_COMMAND_LINE) 190 $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS) 191 192 # Reset target list before populating it 193 $1 := 194 195 ifneq ($$($1_OUTPUT_FILE),) 196 ifneq ($$(words $$($1_SOURCE_FILES)), 1) 197 $$(error Cannot use OUTPUT_FILE for more than one source file (in $1)) 198 endif 199 200 # Note that $1 is space sensitive and must disobey whitespace rules 201 $$(eval $$(call SetupSingleTextFileForProcessing,$1, $$($1_SOURCE_FILES), \ 202 $$(patsubst %/, %, $$(dir $$($1_OUTPUT_FILE))), $$(notdir $$($1_OUTPUT_FILE)))) 203 else 204 ifeq ($$($1_OUTPUT_DIR),) 205 $$(error Neither OUTPUT_FILE nor OUTPUT_DIR was specified (in $1)) 206 endif 207 208 # Now call add_native_source for each source file we are going to process. 209 ifeq ($$($1_SOURCE_BASE_DIR),) 210 # With no base dir specified, put all files in target dir, flattening any 211 # hierarchies. Note that $1 is space sensitive and must disobey whitespace 212 # rules. 213 $$(foreach src, $$($1_SOURCE_FILES), \ 214 $$(eval $$(call SetupSingleTextFileForProcessing,$1, $$(src), \ 215 $$(patsubst %/, %, $$($1_OUTPUT_DIR)), $$(notdir $$(src))))) 216 else 217 # With a base dir, extract the relative portion of the path. Note that $1 218 # is space sensitive and must disobey whitespace rules, and so is the 219 # arguments to patsubst. 220 $$(foreach src, $$($1_SOURCE_FILES), \ 221 $$(eval $$(call SetupSingleTextFileForProcessing,$1, $$(src), \ 222 $$(patsubst %/, %, $$($1_OUTPUT_DIR)), \ 223 $$(patsubst $$($1_SOURCE_BASE_DIR)/%,%,$$(src))))) 224 endif 225 endif 226 endef