]>
Commit | Line | Data |
---|---|---|
1 | # Copyright Deniz Bahadir 2015 | |
2 | # | |
3 | # Distributed under the Boost Software License, Version 1.0. | |
4 | # (See accompanying file LICENSE_1_0.txt or copy at | |
5 | # http://www.boost.org/LICENSE_1_0.txt) | |
6 | # | |
7 | # See http://www.boost.org/libs/mpl for documentation. | |
8 | # See http://stackoverflow.com/a/20660264/3115457 for further information. | |
9 | # See http://stackoverflow.com/a/29627158/3115457 for further information. | |
10 | ||
11 | import fix_boost_mpl_preprocess as fixmpl | |
12 | import argparse | |
13 | import sys | |
14 | import os | |
15 | import os.path | |
16 | import re | |
17 | import fileinput | |
18 | import shutil | |
19 | ||
20 | ||
21 | def create_more_container_files(sourceDir, suffix, maxElements, containers, containers2): | |
22 | """Creates additional files for the individual MPL-containers.""" | |
23 | ||
24 | # Create files for each MPL-container with 20 to 'maxElements' elements | |
25 | # which will be used during generation. | |
26 | for container in containers: | |
27 | for i in range(20, maxElements, 10): | |
28 | # Create copy of "template"-file. | |
29 | newFile = os.path.join( sourceDir, container, container + str(i+10) + suffix ) | |
30 | shutil.copyfile( os.path.join( sourceDir, container, container + "20" + suffix ), newFile ) | |
31 | # Adjust copy of "template"-file accordingly. | |
32 | for line in fileinput.input( newFile, inplace=1, mode="rU" ): | |
33 | line = re.sub(r'20', '%TWENTY%', line.rstrip()) | |
34 | line = re.sub(r'11', '%ELEVEN%', line.rstrip()) | |
35 | line = re.sub(r'10(?![0-9])', '%TEN%', line.rstrip()) | |
36 | line = re.sub(r'%TWENTY%', re.escape(str(i+10)), line.rstrip()) | |
37 | line = re.sub(r'%ELEVEN%', re.escape(str(i + 1)), line.rstrip()) | |
38 | line = re.sub(r'%TEN%', re.escape(str(i)), line.rstrip()) | |
39 | print(line) | |
40 | for container in containers2: | |
41 | for i in range(20, maxElements, 10): | |
42 | # Create copy of "template"-file. | |
43 | newFile = os.path.join( sourceDir, container, container + str(i+10) + "_c" + suffix ) | |
44 | shutil.copyfile( os.path.join( sourceDir, container, container + "20_c" + suffix ), newFile ) | |
45 | # Adjust copy of "template"-file accordingly. | |
46 | for line in fileinput.input( newFile, inplace=1, mode="rU" ): | |
47 | line = re.sub(r'20', '%TWENTY%', line.rstrip()) | |
48 | line = re.sub(r'11', '%ELEVEN%', line.rstrip()) | |
49 | line = re.sub(r'10(?![0-9])', '%TEN%', line.rstrip()) | |
50 | line = re.sub(r'%TWENTY%', re.escape(str(i+10)), line.rstrip()) | |
51 | line = re.sub(r'%ELEVEN%', re.escape(str(i + 1)), line.rstrip()) | |
52 | line = re.sub(r'%TEN%', re.escape(str(i)), line.rstrip()) | |
53 | print(line) | |
54 | ||
55 | ||
56 | def create_input_for_numbered_sequences(headerDir, sourceDir, containers, maxElements): | |
57 | """Creates additional source- and header-files for the numbered sequence MPL-containers.""" | |
58 | # Create additional container-list without "map". | |
59 | containersWithoutMap = containers[:] | |
60 | try: | |
61 | containersWithoutMap.remove('map') | |
62 | except ValueError: | |
63 | # We can safely ignore if "map" is not contained in 'containers'! | |
64 | pass | |
65 | # Create header/source-files. | |
66 | create_more_container_files(headerDir, ".hpp", maxElements, containers, containersWithoutMap) | |
67 | create_more_container_files(sourceDir, ".cpp", maxElements, containers, containersWithoutMap) | |
68 | ||
69 | ||
70 | def adjust_container_limits_for_variadic_sequences(headerDir, containers, maxElements): | |
71 | """Adjusts the limits of variadic sequence MPL-containers.""" | |
72 | for container in containers: | |
73 | headerFile = os.path.join( headerDir, "limits", container + ".hpp" ) | |
74 | regexMatch = r'(define\s+BOOST_MPL_LIMIT_' + container.upper() + r'_SIZE\s+)[0-9]+' | |
75 | regexReplace = r'\g<1>' + re.escape( str(maxElements) ) | |
76 | for line in fileinput.input( headerFile, inplace=1, mode="rU" ): | |
77 | line = re.sub(regexMatch, regexReplace, line.rstrip()) | |
78 | print(line) | |
79 | ||
80 | ||
81 | def current_boost_dir(): | |
82 | """Returns the (relative) path to the Boost source-directory this file is located in (if any).""" | |
83 | # Path to directory containing this script. | |
84 | path = os.path.dirname( os.path.realpath(__file__) ) | |
85 | # Making sure it is located in "${boost-dir}/libs/mpl/preprocessed". | |
86 | for directory in reversed( ["libs", "mpl", "preprocessed"] ): | |
87 | (head, tail) = os.path.split(path) | |
88 | if tail == directory: | |
89 | path = head | |
90 | else: | |
91 | return None | |
92 | return os.path.relpath( path ) | |
93 | ||
94 | ||
95 | ||
96 | def to_positive_multiple_of_10(string): | |
97 | """Converts a string into its encoded positive integer (greater zero) or throws an exception.""" | |
98 | try: | |
99 | value = int(string) | |
100 | except ValueError: | |
101 | msg = '"%r" is not a positive multiple of 10 (greater zero).' % string | |
102 | raise argparse.ArgumentTypeError(msg) | |
103 | if value <= 0 or value % 10 != 0: | |
104 | msg = '"%r" is not a positive multiple of 10 (greater zero).' % string | |
105 | raise argparse.ArgumentTypeError(msg) | |
106 | return value | |
107 | ||
108 | ||
109 | def to_existing_absolute_path(string): | |
110 | """Converts a path into its absolute path and verifies that it exists or throws an exception.""" | |
111 | value = os.path.abspath(string) | |
112 | if not os.path.exists( value ) or not os.path.isdir( value ): | |
113 | msg = '"%r" is not a valid path to a directory.' % string | |
114 | raise argparse.ArgumentTypeError(msg) | |
115 | return value | |
116 | ||
117 | ||
118 | def main(): | |
119 | """The main function.""" | |
120 | ||
121 | # Find the current Boost source-directory in which this script is located. | |
122 | sourceDir = current_boost_dir() | |
123 | if sourceDir == None: | |
124 | sourceDir = "" | |
125 | ||
126 | # Prepare and run cmdline-parser. | |
127 | cmdlineParser = argparse.ArgumentParser(description="A generator-script for pre-processed Boost.MPL headers.") | |
128 | cmdlineParser.add_argument("-v", "--verbose", dest='verbose', action='store_true', | |
129 | help="Be a little bit more verbose.") | |
130 | cmdlineParser.add_argument("-s", "--sequence-type", dest='seqType', choices=['variadic', 'numbered', 'both'], | |
131 | default='both', | |
132 | help="Only update pre-processed headers for the selected sequence types, " | |
133 | "either 'numbered' sequences, 'variadic' sequences or 'both' sequence " | |
134 | "types. (Default=both)") | |
135 | cmdlineParser.add_argument("--no-vector", dest='want_vector', action='store_false', | |
136 | help="Do not update pre-processed headers for Boost.MPL Vector.") | |
137 | cmdlineParser.add_argument("--no-list", dest='want_list', action='store_false', | |
138 | help="Do not update pre-processed headers for Boost.MPL List.") | |
139 | cmdlineParser.add_argument("--no-set", dest='want_set', action='store_false', | |
140 | help="Do not update pre-processed headers for Boost.MPL Set.") | |
141 | cmdlineParser.add_argument("--no-map", dest='want_map', action='store_false', | |
142 | help="Do not update pre-processed headers for Boost.MPL Map.") | |
143 | cmdlineParser.add_argument("--num-elements", dest='numElements', metavar="<num-elements>", | |
144 | type=to_positive_multiple_of_10, default=100, | |
145 | help="The maximal number of elements per container sequence. (Default=100)") | |
146 | cmdlineParser.add_argument(dest='sourceDir', metavar="<source-dir>", default=current_boost_dir(), nargs='?', | |
147 | type=to_existing_absolute_path, | |
148 | help="The source-directory of Boost. (Default=\"" + sourceDir + "\")") | |
149 | args = cmdlineParser.parse_args() | |
150 | ||
151 | # Some verbose debug output. | |
152 | if args.verbose: | |
153 | print "Arguments extracted from command-line:" | |
154 | print " verbose = ", args.verbose | |
155 | print " source directory = ", args.sourceDir | |
156 | print " num elements = ", args.numElements | |
157 | print " sequence type = ", args.seqType | |
158 | print " want: vector = ", args.want_vector | |
159 | print " want: list = ", args.want_list | |
160 | print " want: set = ", args.want_set | |
161 | print " want: map = ", args.want_map | |
162 | ||
163 | # Verify that we received any source-directory. | |
164 | if args.sourceDir == None: | |
165 | print "You should specify a valid path to the Boost source-directory." | |
166 | sys.exit(0) | |
167 | ||
168 | # The directories for header- and source files of Boost.MPL. | |
169 | # NOTE: Assuming 'args.sourceDir' is the source-directory of the entire boost project. | |
170 | headerDir = os.path.join( args.sourceDir, "boost", "mpl" ) | |
171 | sourceDir = os.path.join( args.sourceDir, "libs", "mpl", "preprocessed" ) | |
172 | # Check that the header/source-directories exist. | |
173 | if not os.path.exists( headerDir ) or not os.path.exists( sourceDir ): | |
174 | # Maybe 'args.sourceDir' is not the source-directory of the entire boost project | |
175 | # but instead of the Boost.MPL git-directory, only? | |
176 | headerDir = os.path.join( args.sourceDir, "include", "boost", "mpl" ) | |
177 | sourceDir = os.path.join( args.sourceDir, "preprocessed" ) | |
178 | if not os.path.exists( headerDir ) or not os.path.exists( sourceDir ): | |
179 | cmdlineParser.print_usage() | |
180 | print "error: Cannot find Boost.MPL header/source files in given Boost source-directory!" | |
181 | sys.exit(0) | |
182 | ||
183 | # Some verbose debug output. | |
184 | if args.verbose: | |
185 | print "Chosen header-directory: ", headerDir | |
186 | print "Chosen source-directory: ", sourceDir | |
187 | ||
188 | # Create list of containers for which files shall be pre-processed. | |
189 | containers = [] | |
190 | if args.want_vector: | |
191 | containers.append('vector') | |
192 | if args.want_list: | |
193 | containers.append('list') | |
194 | if args.want_set: | |
195 | containers.append('set') | |
196 | if args.want_map: | |
197 | containers.append('map') | |
198 | if containers == []: | |
199 | print "Nothing to do." | |
200 | print "(Why did you prevent generating pre-processed headers for all Boost.MPL container types?)" | |
201 | sys.exit(0) | |
202 | ||
203 | # Possibly fix the header-comments of input-files needed for pre-processing. | |
204 | if args.verbose: | |
205 | print "Checking if prior to pre-processing some input-files need fixing." | |
206 | needFixing = fixmpl.check_input_files(headerDir, sourceDir, containers, args.seqType, args.verbose) | |
207 | if needFixing: | |
208 | if args.verbose: | |
209 | print "Fixing of some input-files prior to pre-processing is needed." | |
210 | print "Will fix them now!" | |
211 | fixmpl.fix_input_files(headerDir, sourceDir, containers, args.seqType, args.verbose) | |
212 | ||
213 | # Some verbose debug output. | |
214 | if args.verbose: | |
215 | print "Containers for which to pre-process headers: ", containers | |
216 | ||
217 | # Create (additional) input files for generating pre-processed headers of numbered sequence MPL containers. | |
218 | if args.seqType == "both" or args.seqType == "numbered": | |
219 | create_input_for_numbered_sequences(headerDir, sourceDir, containers, args.numElements) | |
220 | # Modify settings for generating pre-processed headers of variadic sequence MPL containers. | |
221 | if args.seqType == "both" or args.seqType == "variadic": | |
222 | adjust_container_limits_for_variadic_sequences(headerDir, containers, args.numElements) | |
223 | ||
224 | # Generate MPL-preprocessed files. | |
225 | os.chdir( sourceDir ) | |
226 | if args.seqType == "both" or args.seqType == "numbered": | |
227 | if args.want_vector: | |
228 | if args.verbose: | |
229 | print "Pre-process headers for Boost.MPL numbered vectors." | |
230 | os.system( "python " + os.path.join( sourceDir, "preprocess_vector.py" ) + " all " + args.sourceDir ) | |
231 | if args.want_list: | |
232 | if args.verbose: | |
233 | print "Pre-process headers for Boost.MPL numbered lists." | |
234 | os.system( "python " + os.path.join( sourceDir, "preprocess_list.py" ) + " all " + args.sourceDir ) | |
235 | if args.want_set: | |
236 | if args.verbose: | |
237 | print "Pre-process headers for Boost.MPL numbered sets." | |
238 | os.system( "python " + os.path.join( sourceDir, "preprocess_set.py" ) + " all " + args.sourceDir ) | |
239 | if args.want_map: | |
240 | if args.verbose: | |
241 | print "Pre-process headers for Boost.MPL numbered maps." | |
242 | os.system( "python " + os.path.join( sourceDir, "preprocess_map.py" ) + " all " + args.sourceDir ) | |
243 | if args.seqType == "both" or args.seqType == "variadic": | |
244 | if args.verbose: | |
245 | print "Pre-process headers for Boost.MPL variadic containers." | |
246 | os.system( "python " + os.path.join( sourceDir, "preprocess.py" ) + " all " + args.sourceDir ) | |
247 | ||
248 | ||
249 | if __name__ == '__main__': | |
250 | main() |