6 -- Lua 5.2 module providing a mingw-get setup hook for configuration of
7 -- the user's MinGW GCC compiler <features.h> preferences.
9 -- Written by Keith Marshall <keithmarshall@users.sourceforge.net>
10 -- Copyright (C) 2019, MinGW.org Project
13 -- Permission is hereby granted, free of charge, to any person obtaining a
14 -- copy of this software and associated documentation files (the "Software"),
15 -- to deal in the Software without restriction, including without limitation
16 -- the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 -- and/or sell copies of the Software, and to permit persons to whom the
18 -- Software is furnished to do so, subject to the following conditions:
20 -- The above copyright notice and this permission notice shall be included
21 -- in all copies or substantial portions of the Software.
23 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 -- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 -- DEALINGS IN THE SOFTWARE.
32 -- We begin by initializing a container, for construction of a Lua module
33 -- to encapsulate the content of this source file.
37 -- mingw-get passes the MinGW installation root directory path, in the
38 -- $MINGW32_SYSROOT environment variable; from this, we deduce the path
39 -- name for the working copy of the features.h file...
41 local function syspath( varname )
43 -- ...using this local helper function to ensure that the path name
44 -- string, returned from the environment, is free from insignificant
45 -- trailing directory name separators, and that all internal sequences
46 -- of directory name separators are normalized to a single '/'.
48 local pathname = os.getenv( varname )
51 pathname = string.gsub( pathname, "[/\\]+", "/" )
52 pathname = string.match( pathname, "(.*[^/])/*$" )
56 local sysroot = syspath( "MINGW32_SYSROOT" )
59 error( "environment variable MINGW32_SYSROOT may not be set", 0 )
61 local config_file_name = sysroot .. "/include/features.h"
63 -- Define a template, whence the default features configuration may be
64 -- deduced when writing the initial content of the <features.h> file.
66 local config_default =
70 ' * Features configuration for MinGW.org GCC implementation; users may',
71 ' * customize this file, to establish their preferred default behaviour.',
72 ' * Projects may provide an alternative, package-specific configuration,',
73 ' * either by placing their own customized <features.h> in the package',
74 ' * -I path, ahead of the system default, or by assignment of their',
75 ' * preferred alternative to the _MINGW_FEATURES_HEADER macro.',
80 ' * Template written by Keith Marshall <keith@users.osdn.me>',
81 ' * Copyright (C) 2019, MinGW.org Project.',
84 ' * Permission is hereby granted, free of charge, to any person obtaining a',
85 ' * copy of this software and associated documentation files (the "Software"),',
86 ' * to deal in the Software without restriction, including without limitation',
87 ' * the rights to use, copy, modify, merge, publish, distribute, sublicense,',
88 ' * and/or sell copies of the Software, and to permit persons to whom the',
89 ' * Software is furnished to do so, subject to the following conditions:',
91 ' * The above copyright notice, this permission notice, and the following',
92 ' * disclaimer shall be included in all copies or substantial portions of',
95 ' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS',
96 ' * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,',
97 ' * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL',
98 ' * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER',
99 ' * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING',
100 ' * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER',
101 ' * DEALINGS IN THE SOFTWARE.',
104 '#ifndef __MINGW_FEATURES__',
105 '#pragma GCC system_header',
107 '/* Users are expected to customize this header, but it remains subject to',
108 ' * automatic updates by system software. To ensure that any customisation',
109 ' * is not ovewritten, during such updates, it MUST observe the following:',
111 ' * This header MUST define __MINGW_FEATURES__; the definition MUST begin',
112 ' * with "#define __MINGW_FEATURES__ (__MINGW_FEATURES_BEGIN__) \\"; it MUST',
113 ' * extend over multiple lines, and terminate with "__MINGW_FEATURES_END__";',
114 ' * intervening lines may enumerate any defined features, one per line, and',
115 ' * each specified as an argument to either the __MINGW_FEATURE_ENABLE__(),',
116 ' * or the __MINGW_FEATURE_IGNORE__() macro, (ensuring that at least one',
117 ' * space separates either of these macro names from its parenthesized',
118 ' * argument name).',
121 ' * If customizing this features configuration, ALWAYS refer to features',
122 ' * using their designated symbolic constant names; NEVER usurp the use of',
123 ' * these symbolic constants for any other purpose, and NEVER assume that',
124 ' * any such constant has a specific value ... their definitions may vary',
125 ' * between distinct MinGW Runtime Library software releases!',
127 '#define __MINGW_FEATURES__ (__MINGW_FEATURES_BEGIN__) \\',
128 ' __MINGW_FEATURE_IGNORE__ (__MINGW_ANSI_STDIO__) \\',
129 ' __MINGW_FEATURE_IGNORE__ (__MINGW_LC_MESSAGES__) \\',
130 ' __MINGW_FEATURE_IGNORE__ (__MINGW_LC_ENVVARS__) \\',
131 ' __MINGW_FEATURES_END__',
133 '#endif /* !__MINGW_FEATURES__: $'..'RCSfile$: end of file */'
136 local function defines( name )
138 -- A local helper function, to generate a string.match
139 -- pattern for identification of any C #define statement,
140 -- which defines a specified symbol "name"
142 return "^%s*#%s*define%s+" .. name
145 local function feature( value )
147 -- A local helper function, to generate a string.match
148 -- pattern for identification of a specified parenthesized
149 -- value field, within a C #define statement.
151 return "%s+%(%s*" .. value .. "%s*%)%s*"
154 local function begins_definition( line, name, value )
156 -- A local helper function to check whether any input "line"
157 -- represents the first of a multiline C #define for the "name"
158 -- symbol, with its initial field matching the parenthesized
159 -- "value" token (default: "__MINGW_FEATURES_BEGIN__").
161 if not value then value = "__MINGW_FEATURES_BEGIN__" end
162 return string.match( line, defines( name ) .. feature( value ) .. "\\$" )
165 -- In the event that a features configuration has already been
166 -- specified for this installation, capture this into internal
167 -- "as built" configuration tables...
169 local current_config, current_features
170 local config = io.open( config_file_name )
173 -- ...always starting collection into table "current_config"...
176 local active_list = current_config
178 -- ...reading the existing configuration file, line by line...
180 for line in config:lines()
181 do if active_list == current_features
182 and string.match( line, "^%s*__MINGW_FEATURES_END__%s*$" )
184 -- ...noting that actual configuration options will have
185 -- been diverted to the "current_features" table; when all
186 -- such options have been captured, redirect any residual
187 -- content to the "current_config" table.
189 active_list = current_config
192 -- Capture the current configuration record, into whichever
193 -- diversion is currently active.
195 table.insert( active_list, line )
197 if begins_definition( line, "__MINGW_FEATURES__" )
199 -- When we find the first line of a (possibly) well-formed
200 -- features configuration, divert capture of its subsequent
201 -- lines to a new "current_features" table.
203 current_features = {}
204 active_list = current_features
208 -- When capture is complete, ensure that the input stream file
209 -- is closed; (we may want to reopen it later, to rewrite it).
213 -- Before we go any further, check for well-formedness of the
214 -- configuration which we have just captured; effectively...
216 if not current_features
217 or active_list == current_features
219 -- ...when no "current_features" diversion has been created,
220 -- or such a diversion remains open for capture, then an error
221 -- has occurred, and the configuration is not well-formed.
223 local function config_error( reason )
224 error( config_file_name .." ".. reason, 0 )
226 for ref, line in next, current_config
227 do if string.match( line, defines( "__MINGW_FEATURES__" ) )
229 -- In this case, a "__MINGW_FEATURES__" definition is
230 -- present, but it is not well-formed; (note that, here,
231 -- we rescan the "current_config" table, using a less
232 -- rigorous criterion for detection of a definition of
233 -- of "__MINGW_FEATURES__" than that which is required
234 -- to open the "current_features" diversion)...
236 config_error( "has malformed __MINGW_FEATURES__ definition" )
239 -- ...while in this case, no "__MINGW_FEATURES__" definition
240 -- was found, (well-formed, or otherwise).
242 config_error( "does not define __MINGW_FEATURES__" )
247 local function update_configuration( stream_file, template, current )
249 -- A function to write a features configuration to a designated output
250 -- stream, based on a specified template, reproducing and encapsulating
251 -- any existing configuration which may also have been specified...
253 local function stream_file_writeln( line )
255 -- ...using this helper function to write each line.
257 stream_file:write( line .. "\n" )
262 -- An existing configuration header is to be updated. An image of
263 -- its original content has already been loaded into "current"; copy
264 -- it back to the original file, line by line...
266 for ref, line in next, current
267 do if begins_definition( line, "__MINGW_FEATURES__" )
269 -- ...until we reach the first active configuration record.
270 -- We now wish to merge any new configuration options from the
271 -- template, into the existing configuration; work through the
272 -- template, line by line, ignoring all lines...
275 for ref, sub in next, template
276 do if begins_definition( sub, "__MINGW_FEATURES__" )
278 -- ...until we find the first line of a (possibly)
279 -- well-formed features configuration record; when we
280 -- find this, we decompose and reassemble it, so that
281 -- we may preserve a tabulation format matching the
282 -- longer of the template and actual configuration
285 local def = string.match( sub, "^[^(]*" )
286 local usr = string.match( line, "^[^(]*" )
287 if string.len( usr ) > string.len( def )
291 usr = string.match( line, "%(.*" )
292 sub = string.match( sub, "%(.*" )
293 if string.len( usr ) > string.len( sub )
298 -- Write out the reassembled features configuration
299 -- start record, and activate the features merge.
301 stream_file_writeln( def .. sub )
304 -- During the merge operation, when we find the record
305 -- terminator within the template...
307 elseif string.match( sub, "^%s*__MINGW_FEATURES_END__%s*$" )
309 -- ...we immediately write out any additional option
310 -- specifications, which remain in the user specified
313 for ref, sub in next, current_features
315 stream_file_writeln( sub )
318 -- ...and break out of the merge cycle.
324 -- While we remain within the merge cycle, we extract
325 -- the identification of each configuration option from
326 -- the template, comparing it with all entries in the
327 -- "current_features" table...
329 tag = string.match( sub, "%(.*%)" )
330 for ref, usr in next, current_features
331 do if string.match( usr, tag )
334 -- ...and, when we find a match, we favour the
335 -- existing configuration record over that from
340 -- ...remove the corresponding entry from the
341 -- "current_features" table, and break out of
342 -- the inner matching loop, before...
344 table.remove( current_features, ref )
349 -- ...in either event, writing out a copy of each
350 -- selected configuration record.
352 stream_file_writeln( sub )
357 -- For every line in the existing configuration file, outside
358 -- the scope of the "__MINGW_FEATURES__" definition itself, we
359 -- simply write out a verbatim copy of each line.
361 stream_file_writeln( line )
366 -- There is no existing configuration header, so we simply reproduce
367 -- the template, processing it line by line...
369 for ref, line in next, template
371 -- ...and writing out each line individually.
373 stream_file_writeln( line )
379 function M.pathname( suffix )
381 -- An exported utility function, to facilitate identification of
382 -- the full MS-Windows path name for the features.h configuration
383 -- file, as appropriate to the current installation...
387 -- ...appending any suffix which may have been specified, (e.g.
388 -- to specify a reference to the features.h.sample file)...
390 return config_file_name .. suffix
393 -- ...otherwise, specifying a reference to features.h itself.
395 return config_file_name
399 function M.initialize( stream_file )
401 -- Primary initialization function; overwrites any existing features
402 -- configuration header, opened for writing as "stream_file"...
406 -- ...or falling back to "io.stderr", in the event that no output
407 -- stream file has been opened...
409 stream_file = io.stdout
412 -- ...replacing any existing configuration with an exact copy of the
413 -- "config_default" content specified within this module.
415 update_configuration( stream_file, config_default )
419 function M.update( stream_file )
421 -- Primary API function, exported for use by mingw-get (or any other
422 -- client); overwrites any existing configuration header file, which
423 -- has been opened for writing as "stream_file"...
427 -- ...or falling back to "io.stderr", in the event that no output
428 -- stream file has been opened...
430 stream_file = io.stdout
433 -- ...preserving any existing configuration, with the addition of
434 -- any configuration options which have been included in the default
435 -- template, but which do not yet appear in the existing header.
437 update_configuration( stream_file, config_default, current_config )
440 -- Since this source file is intended to be loaded as a Lua module, we
441 -- must ultimately return a reference handle for it.
445 -- $RCSfile$: end of file */