OSDN Git Service

Incorporate user-customized <features.h> configuration.
[mingw/mingw-org-wsl.git] / var / features.lua
1 --
2 -- features.lua
3 --
4 -- $Id$
5 --
6 -- Lua 5.2 module providing a mingw-get setup hook for configuration of
7 -- the user's MinGW GCC compiler <features.h> preferences.
8 --
9 -- Written by Keith Marshall <keithmarshall@users.sourceforge.net>
10 -- Copyright (C) 2019, MinGW.org Project
11 --
12 --
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:
19 --
20 -- The above copyright notice and this permission notice shall be included
21 -- in all copies or substantial portions of the Software.
22 --
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.
30 --
31 --
32 -- We begin by initializing a container, for construction of a Lua module
33 -- to encapsulate the content of this source file.
34 --
35    local M = {}
36 --
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...
40 --
41    local function syspath( varname )
42 --
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 '/'.
47 --
48      local pathname = os.getenv( varname )
49      if pathname
50      then
51        pathname = string.gsub( pathname, "[/\\]+", "/" )
52        pathname = string.match( pathname, "(.*[^/])/*$" )
53      end
54      return pathname
55    end
56    local sysroot = syspath( "MINGW32_SYSROOT" )
57    if not sysroot
58    then
59      error( "environment variable MINGW32_SYSROOT may not be set", 0 )
60    end
61    local config_file_name = sysroot .. "/include/features.h"
62 --
63 -- Define a template, whence the default features configuration may be
64 -- deduced when writing the initial content of the <features.h> file.
65 --
66    local config_default =
67    { '/*',
68      ' * features.h',
69      ' *',
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.',
76      ' *',
77      ' *',
78      ' * $'..'Id$',
79      ' *',
80      ' * Template written by Keith Marshall <keith@users.osdn.me>',
81      ' * Copyright (C) 2019, MinGW.org Project.',
82      ' *',
83      ' *',
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:',
90      ' *',
91      ' * The above copyright notice, this permission notice, and the following',
92      ' * disclaimer shall be included in all copies or substantial portions of',
93      ' * the Software.',
94      ' *',
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.',
102      ' *',
103      ' */',
104      '#ifndef __MINGW_FEATURES__',
105      '#pragma GCC system_header',
106      '',
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:',
110      ' *',
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).',
119      ' *',
120      ' * CAUTION:',
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!',
126      ' */',
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__',
132      '',
133      '#endif    /* !__MINGW_FEATURES__: $'..'RCSfile$: end of file */'
134    }
135 --
136    local function defines( name )
137 --
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"
141 --
142      return "^%s*#%s*define%s+" .. name
143    end
144 --
145    local function feature( value )
146 --
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.
150 --
151      return "%s+%(%s*" .. value .. "%s*%)%s*"
152    end
153 --
154    local function begins_definition( line, name, value )
155 --
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__").
160 --
161      if not value then value = "__MINGW_FEATURES_BEGIN__" end
162      return string.match( line, defines( name ) .. feature( value ) .. "\\$" )
163    end
164 --
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...
168 --
169    local current_config, current_features
170    local config = io.open( config_file_name )
171    if config
172    then
173 --   ...always starting collection into table "current_config"...
174 --
175      current_config = {}
176      local active_list = current_config
177 --
178 --   ...reading the existing configuration file, line by line...
179 --
180      for line in config:lines()
181      do if active_list == current_features
182         and string.match( line, "^%s*__MINGW_FEATURES_END__%s*$" )
183         then
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.
188 --
189           active_list = current_config
190         end
191 --
192 --      Capture the current configuration record, into whichever
193 --      diversion is currently active.
194 --
195         table.insert( active_list, line )
196 --
197         if begins_definition( line, "__MINGW_FEATURES__" )
198         then
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.
202 --
203           current_features = {}
204           active_list = current_features
205         end
206      end
207 --
208 --   When capture is complete, ensure that the input stream file
209 --   is closed; (we may want to reopen it later, to rewrite it).
210 --
211      io.close( config )
212 --
213 --   Before we go any further, check for well-formedness of the
214 --   configuration which we have just captured; effectively...
215 --
216      if not current_features
217      or active_list == current_features
218      then
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.
222 --
223        local function config_error( reason )
224          error( config_file_name .." ".. reason, 0 )
225        end
226        for ref, line in next, current_config
227        do if string.match( line, defines( "__MINGW_FEATURES__" ) )
228           then
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)...
235 --
236             config_error( "has malformed __MINGW_FEATURES__ definition" )
237           end
238        end
239 --     ...while in this case, no "__MINGW_FEATURES__" definition
240 --     was found, (well-formed, or otherwise).
241 --
242        config_error( "does not define __MINGW_FEATURES__" )
243      end
244    end
245 --
246 --
247    local function update_configuration( stream_file, template, current )
248 --
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...
252 --
253      local function stream_file_writeln( line )
254 --
255 --     ...using this helper function to write each line.
256 --
257        stream_file:write( line .. "\n" )
258      end
259 --
260      if current
261      then
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...
265 --
266        for ref, line in next, current
267        do if begins_definition( line, "__MINGW_FEATURES__" )
268           then
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...
273 --
274             local merge = false
275             for ref, sub in next, template
276             do if begins_definition( sub, "__MINGW_FEATURES__" )
277                then
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
283 --               records.
284 --
285                  local def = string.match( sub, "^[^(]*" )
286                  local usr = string.match( line, "^[^(]*" )
287                  if string.len( usr ) > string.len( def )
288                  then
289                    def = usr
290                  end
291                  usr = string.match( line, "%(.*" )
292                  sub = string.match( sub, "%(.*" )
293                  if string.len( usr ) > string.len( sub )
294                  then
295                    sub = usr
296                  end
297 --
298 --               Write out the reassembled features configuration
299 --               start record, and activate the features merge.
300 --
301                  stream_file_writeln( def .. sub )
302                  merge = true
303 --
304 --             During the merge operation, when we find the record
305 --             terminator within the template...
306 --
307                elseif string.match( sub, "^%s*__MINGW_FEATURES_END__%s*$" )
308                then
309 --               ...we immediately write out any additional option
310 --               specifications, which remain in the user specified
311 --               configuration...
312 --
313                  for ref, sub in next, current_features
314                  do
315                    stream_file_writeln( sub )
316                  end
317 --
318 --               ...and break out of the merge cycle.
319 --
320                  break
321 --
322                elseif merge
323                then
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...
328 --
329                  tag = string.match( sub, "%(.*%)" )
330                  for ref, usr in next, current_features
331                  do if string.match( usr, tag )
332                     then
333 --
334 --                    ...and, when we find a match, we favour the
335 --                    existing configuration record over that from
336 --                    the template...
337 --
338                       sub = usr
339 --
340 --                    ...remove the corresponding entry from the
341 --                    "current_features" table, and break out of
342 --                    the inner matching loop, before...
343 --
344                       table.remove( current_features, ref )
345                       break
346                     end
347                  end
348 --
349 --               ...in either event, writing out a copy of each
350 --               selected configuration record.
351 --
352                  stream_file_writeln( sub )
353                end
354             end
355           else
356 --
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.
360 --
361             stream_file_writeln( line )
362           end
363        end
364      else
365 --
366 --     There is no existing configuration header, so we simply reproduce
367 --     the template, processing it line by line...
368 --
369        for ref, line in next, template
370        do
371 --       ...and writing out each line individually.
372 --
373          stream_file_writeln( line )
374        end
375      end
376    end
377 --
378 --
379    function M.pathname( suffix )
380 --
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...
384 --
385      if suffix
386      then
387 --     ...appending any suffix which may have been specified, (e.g.
388 --     to specify a reference to the features.h.sample file)...
389 --
390        return config_file_name .. suffix
391      end
392 --
393 --   ...otherwise, specifying a reference to features.h itself.
394 --
395      return config_file_name
396    end
397 --
398 --
399    function M.initialize( stream_file )
400 --
401 --   Primary initialization function; overwrites any existing features
402 --   configuration header, opened for writing as "stream_file"...
403 --
404      if not stream_file
405      then
406 --     ...or falling back to "io.stderr", in the event that no output
407 --     stream file has been opened...
408 --
409        stream_file = io.stdout
410      end
411 --
412 --   ...replacing any existing configuration with an exact copy of the
413 --   "config_default" content specified within this module.
414 --
415      update_configuration( stream_file, config_default )
416    end
417 --
418 --
419    function M.update( stream_file )
420 --
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"...
424 --
425      if not stream_file
426      then
427 --     ...or falling back to "io.stderr", in the event that no output
428 --     stream file has been opened...
429 --
430        stream_file = io.stdout
431      end
432 --
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.
436 --
437      update_configuration( stream_file, config_default, current_config )
438    end
439 --
440 -- Since this source file is intended to be loaded as a Lua module, we
441 -- must ultimately return a reference handle for it.
442 --
443    return M
444 --
445 -- $RCSfile$: end of file */