]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // (C) Copyright John Maddock 2004. |
2 | // Use, modification and distribution are subject to the | |
3 | // Boost Software License, Version 1.0. (See accompanying file | |
4 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | // | |
7 | // This progam scans for *.ipp files in the libs/config/test | |
8 | // directory and then generates the *.cpp test files from them | |
9 | // along with config_test.cpp and a Jamfile. | |
10 | // | |
11 | ||
12 | #include <boost/regex.hpp> | |
13 | #include <boost/filesystem/path.hpp> | |
14 | #include <boost/filesystem/operations.hpp> | |
15 | #include <boost/filesystem/fstream.hpp> | |
16 | #include <boost/detail/lightweight_main.hpp> | |
17 | #include <iostream> | |
18 | #include <sstream> | |
19 | #include <string> | |
20 | #include <set> | |
21 | #include <ctime> | |
22 | ||
23 | namespace fs = boost::filesystem; | |
24 | ||
25 | fs::path config_path; | |
26 | ||
27 | std::string copyright( | |
28 | "// Copyright John Maddock 2002-4.\n" | |
29 | "// Use, modification and distribution are subject to the \n" | |
30 | "// Boost Software License, Version 1.0. (See accompanying file \n" | |
31 | "// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n" | |
32 | "\n" | |
33 | "// See http://www.boost.org/libs/config for the most recent version." | |
34 | "//\n// Revision $Id$\n//\n"); | |
35 | ||
36 | std::stringstream config_test1; | |
37 | std::stringstream config_test1a; | |
38 | std::stringstream config_test2; | |
39 | std::stringstream jamfile; | |
40 | std::stringstream jamfile_v2; | |
41 | std::stringstream build_config_test; | |
42 | std::stringstream build_config_jamfile; | |
43 | std::set<std::string> macro_list; | |
b32b8144 | 44 | std::set<std::string> feature_list; |
7c673cae FG |
45 | |
46 | void write_config_info() | |
47 | { | |
48 | // load the file into memory so we can scan it: | |
49 | fs::ifstream ifs(config_path / "config_info.cpp"); | |
50 | std::string file_text; | |
51 | std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(), std::back_inserter(file_text)); | |
52 | ifs.close(); | |
53 | // create macro list: | |
54 | std::stringstream ss; | |
55 | for(std::set<std::string>::const_iterator i(macro_list.begin()), j(macro_list.end()); | |
56 | i != j; | |
57 | ++i) | |
58 | { | |
59 | ss << " PRINT_MACRO(" << *i << ");\n"; | |
60 | } | |
61 | std::string macros = ss.str(); | |
62 | // scan for Boost macro block: | |
63 | boost::regex re("BEGIN\\s+GENERATED\\s+BLOCK\\s+DO\\s+NOT\\s+EDIT\\s+THIS[^\\n]+\\n(.*?)\\n\\s+//\\s*END\\s+GENERATED\\s+BLOCK"); | |
64 | boost::smatch what; | |
65 | if(boost::regex_search(file_text, what, re)) | |
66 | { | |
67 | std::string new_text; | |
68 | new_text.append(what.prefix().first, what[1].first); | |
69 | new_text.append(macros); | |
70 | new_text.append(what[1].second, what.suffix().second); | |
71 | fs::ofstream ofs(config_path / "config_info.cpp"); | |
72 | ofs << new_text; | |
73 | } | |
74 | } | |
75 | ||
76 | void write_config_test() | |
77 | { | |
78 | fs::ofstream ofs(config_path / "config_test.cpp"); | |
79 | time_t t = std::time(0); | |
80 | ofs << "// This file was automatically generated on " << std::ctime(&t); | |
81 | ofs << "// by libs/config/tools/generate.cpp\n" << copyright << std::endl; | |
82 | ofs << "// Test file for config setup\n" | |
83 | "// This file should compile, if it does not then\n" | |
84 | "// one or more macros need to be defined.\n" | |
85 | "// see boost_*.ipp for more details\n\n" | |
86 | "// Do not edit this file, it was generated automatically by\n\n" | |
87 | "#include <boost/config.hpp>\n#include <iostream>\n#include \"test.hpp\"\n\n" | |
88 | "int error_count = 0;\n\n"; | |
89 | ofs << config_test1.str() << std::endl; | |
90 | ofs << config_test1a.str() << std::endl; | |
91 | ofs << "int main( int, char *[] )\n{\n" << config_test2.str() << " return error_count;\n}\n\n"; | |
92 | } | |
93 | ||
94 | void write_jamfile_v2() | |
95 | { | |
96 | fs::ofstream ofs(config_path / "all" / "Jamfile.v2"); | |
97 | time_t t = std::time(0); | |
98 | ofs << "#\n# Regression test Jamfile for boost configuration setup.\n# *** DO NOT EDIT THIS FILE BY HAND ***\n" | |
99 | "# This file was automatically generated on " << std::ctime(&t); | |
100 | ofs << "# by libs/config/tools/generate.cpp\n" | |
101 | "# Copyright John Maddock.\n" | |
102 | "# Use, modification and distribution are subject to the \n" | |
103 | "# Boost Software License, Version 1.0. (See accompanying file \n" | |
104 | "# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n" | |
105 | "#\n# If you need to alter build preferences then set them in\n" | |
106 | "# the template defined in options_v2.jam.\n#\n" | |
107 | "path-constant DOT : . ;\n" | |
108 | "include $(DOT)/options_v2.jam ;\n\n" | |
109 | "run ../config_info.cpp : : : <threading>single <toolset>msvc:<runtime-link>static <toolset>msvc:<link>static ;\n" | |
110 | "run ../config_info.cpp : : : <threading>multi : config_info_threaded ;\n" | |
111 | "run ../math_info.cpp : : : <toolset>borland:<runtime-link>static <toolset>borland:<link>static ;\n" | |
112 | "run ../config_test.cpp : : : <threading>single <toolset>msvc:<runtime-link>static <toolset>msvc:<link>static ;\n" | |
113 | "run ../config_test.cpp : : : <threading>multi : config_test_threaded ;\n" | |
114 | "run ../limits_test.cpp ../../../test/build//boost_test_exec_monitor ;\n" | |
115 | "run ../abi/abi_test.cpp ../abi/main.cpp ;\n\n"; | |
116 | ofs << jamfile_v2.str() << std::endl; | |
117 | ||
118 | } | |
119 | ||
120 | void write_test_file(const fs::path& file, | |
121 | const std::string& macro_name, | |
122 | const std::string& namespace_name, | |
123 | const std::string& header_file, | |
124 | bool positive_test, | |
125 | bool expect_success) | |
126 | { | |
127 | if(!fs::exists(file)) | |
128 | { | |
129 | std::cout << "Writing test file " << file.string() << std::endl; | |
130 | ||
131 | fs::ofstream ofs(file); | |
132 | std::time_t t = std::time(0); | |
133 | ofs << "// This file was automatically generated on " << std::ctime(&t); | |
134 | ofs << "// by libs/config/tools/generate.cpp\n" << copyright << std::endl; | |
135 | ofs << "\n// Test file for macro " << macro_name << std::endl; | |
136 | ||
137 | if(expect_success) | |
138 | { | |
139 | ofs << "// This file should compile, if it does not then\n" | |
140 | "// " << macro_name << " should "; | |
141 | if(positive_test) | |
142 | ofs << "not "; | |
143 | ofs << "be defined.\n"; | |
144 | } | |
145 | else | |
146 | { | |
147 | ofs << "// This file should not compile, if it does then\n" | |
148 | "// " << macro_name << " should "; | |
149 | if(!positive_test) | |
150 | ofs << "not "; | |
151 | ofs << "be defined.\n"; | |
152 | } | |
153 | ofs << "// See file " << header_file << " for details\n\n"; | |
154 | ||
155 | ofs << "// Must not have BOOST_ASSERT_CONFIG set; it defeats\n" | |
156 | "// the objective of this file:\n" | |
157 | "#ifdef BOOST_ASSERT_CONFIG\n" | |
158 | "# undef BOOST_ASSERT_CONFIG\n" | |
159 | "#endif\n\n"; | |
160 | ||
161 | static const boost::regex tr1_exp("BOOST_HAS_TR1.*"); | |
162 | ||
163 | ofs << "#include <boost/config.hpp>\n"; | |
164 | ||
7c673cae FG |
165 | ofs << "#include \"test.hpp\"\n\n" |
166 | "#if"; | |
167 | if(positive_test != expect_success) | |
168 | ofs << "n"; | |
169 | ofs << "def " << macro_name << | |
170 | "\n#include \"" << header_file << | |
171 | "\"\n#else\n"; | |
172 | if(expect_success) | |
173 | ofs << "namespace " << namespace_name << " = empty_boost;\n"; | |
174 | else | |
175 | ofs << "#error \"this file should not compile\"\n"; | |
176 | ofs << "#endif\n\n"; | |
177 | ||
178 | ofs << "int main( int, char *[] )\n{\n return " << namespace_name << "::test();\n}\n\n"; | |
179 | } | |
180 | else | |
181 | { | |
182 | std::cout << "Skipping existing test file " << file.string() << std::endl; | |
183 | } | |
184 | } | |
185 | ||
186 | void write_build_tests() | |
187 | { | |
188 | fs::ofstream ofs(config_path / ".." / "checks" / "test_case.cpp"); | |
189 | time_t t = std::time(0); | |
190 | ofs << "// This file was automatically generated on " << std::ctime(&t); | |
191 | ofs << "// by libs/config/tools/generate.cpp\n" << copyright << std::endl; | |
b32b8144 | 192 | ofs << "#include <boost/config.hpp>\n\n"; |
7c673cae | 193 | ofs << build_config_test.str() << std::endl; |
b32b8144 | 194 | ofs << "int main( int, char *[] )\n{\n" << " return 0;\n}\n\n"; |
7c673cae FG |
195 | } |
196 | ||
197 | void write_build_check_jamfile() | |
198 | { | |
199 | fs::ofstream ofs(config_path / ".." / "checks" / "Jamfile.v2"); | |
200 | time_t t = std::time(0); | |
201 | ofs << "#\n# *** DO NOT EDIT THIS FILE BY HAND ***\n" | |
202 | "# This file was automatically generated on " << std::ctime(&t); | |
203 | ofs << "# by libs/config/tools/generate.cpp\n" | |
204 | "# Copyright John Maddock.\n" | |
205 | "# Use, modification and distribution are subject to the \n" | |
206 | "# Boost Software License, Version 1.0. (See accompanying file \n" | |
207 | "# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n\n" | |
208 | "import modules ;\nimport path ; \n\n" | |
209 | "\n" | |
7c673cae FG |
210 | ; |
211 | ofs << build_config_jamfile.str() << std::endl; | |
212 | } | |
213 | ||
214 | void process_ipp_file(const fs::path& file, bool positive_test) | |
215 | { | |
216 | std::cout << "Info: Scanning file: " << file.string() << std::endl; | |
217 | ||
218 | // our variables: | |
219 | std::string file_text; | |
220 | std::string macro_name; | |
221 | std::string namespace_name; | |
222 | fs::path positive_file; | |
223 | fs::path negative_file; | |
224 | ||
225 | // load the file into memory so we can scan it: | |
226 | fs::ifstream ifs(file); | |
227 | std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(), std::back_inserter(file_text)); | |
228 | ifs.close(); | |
229 | // scan for the macro name: | |
230 | boost::regex macro_regex("//\\s*MACRO\\s*:\\s*(\\w+)"); | |
231 | boost::smatch macro_match; | |
232 | if(boost::regex_search(file_text, macro_match, macro_regex)) | |
233 | { | |
234 | macro_name = macro_match[1]; | |
235 | macro_list.insert(macro_name); | |
236 | namespace_name = boost::regex_replace(file_text, macro_regex, "\\L$1", boost::format_first_only | boost::format_no_copy); | |
237 | } | |
238 | if(macro_name.empty()) | |
239 | { | |
240 | std::cout << "Error: no macro definition found in " << file.string(); | |
241 | } | |
242 | else | |
243 | { | |
244 | std::cout << "Info: Macroname: " << macro_name << std::endl; | |
245 | } | |
246 | ||
247 | // get the output filesnames: | |
248 | boost::regex file_regex("boost_([^.]+)\\.ipp"); | |
249 | positive_file = file.branch_path() / boost::regex_replace(file.leaf().string(), file_regex, "$1_pass.cpp"); | |
250 | negative_file = file.branch_path() / boost::regex_replace(file.leaf().string(), file_regex, "$1_fail.cpp"); | |
251 | write_test_file(positive_file, macro_name, namespace_name, file.leaf().string(), positive_test, true); | |
252 | write_test_file(negative_file, macro_name, namespace_name, file.leaf().string(), positive_test, false); | |
253 | ||
254 | // always create config_test data, | |
255 | // positive and negative tests go to separate streams, because for some | |
256 | // reason some compilers choke unless we put them in a particular order... | |
257 | std::ostream* pout = positive_test ? &config_test1a : &config_test1; | |
258 | *pout << "#if"; | |
259 | if(!positive_test) | |
260 | *pout << "n"; | |
261 | *pout << "def " << macro_name | |
262 | << "\n#include \"" << file.leaf().string() << "\"\n#else\nnamespace " | |
263 | << namespace_name << " = empty_boost;\n#endif\n"; | |
264 | ||
265 | config_test2 << " if(0 != " << namespace_name << "::test())\n" | |
266 | " {\n" | |
267 | " std::cerr << \"Failed test for " << macro_name << " at: \" << __FILE__ << \":\" << __LINE__ << std::endl;\n" | |
268 | " ++error_count;\n" | |
269 | " }\n"; | |
270 | ||
271 | // always generate the jamfile data: | |
272 | jamfile << "test-suite \"" << macro_name << "\" : \n" | |
273 | "[ run " << positive_file.leaf().string() << " <template>config_options ]\n" | |
274 | "[ compile-fail " << negative_file.leaf().string() << " <template>config_options ] ;\n"; | |
275 | ||
276 | jamfile_v2 << "test-suite \"" << macro_name << "\" : \n" | |
277 | "[ run ../" << positive_file.leaf().string() << " ]\n" | |
278 | "[ compile-fail ../" << negative_file.leaf().string() << " ] ;\n"; | |
279 | ||
280 | // Generate data for the Build-checks test file: | |
281 | build_config_test << "#ifdef TEST_" << macro_name << std::endl; | |
b32b8144 FG |
282 | if (positive_test) |
283 | { | |
284 | build_config_test << "# ifndef " << macro_name << "\n# error \"Feature macro " << macro_name << " is not defined.\"\n# endif\n"; | |
285 | } | |
286 | else | |
287 | { | |
288 | build_config_test << "# ifdef " << macro_name << "\n# error \"Defect macro " << macro_name << " is defined.\"\n# endif\n"; | |
289 | } | |
290 | build_config_test << "#endif\n"; | |
7c673cae FG |
291 | |
292 | // Generate data for the build-checks Jamfile: | |
293 | static const boost::regex feature_regex("boost_(?:no|has)_(.*)"); | |
294 | std::string feature_name = boost::regex_replace(namespace_name, feature_regex, "\\1"); | |
b32b8144 FG |
295 | if(feature_list.find(feature_name) == feature_list.end()) |
296 | build_config_jamfile << "obj " << feature_name << " : test_case.cpp : <define>TEST_" << macro_name << " ;\n"; | |
297 | feature_list.insert(feature_name); | |
7c673cae FG |
298 | } |
299 | ||
300 | int cpp_main(int argc, char* argv[]) | |
301 | { | |
302 | // | |
303 | // get the boost path to begin with: | |
304 | // | |
305 | if(argc > 1) | |
306 | { | |
307 | fs::path p(argv[1]); | |
308 | config_path = p / "libs" / "config" / "test" ; | |
309 | } | |
310 | else | |
311 | { | |
312 | // try __FILE__: | |
313 | fs::path p(__FILE__); | |
314 | config_path = p.branch_path().branch_path() / "test"; | |
315 | } | |
316 | std::cout << "Info: Boost.Config test path set as: " << config_path.string() << std::endl; | |
317 | ||
318 | // enumerate *.ipp files and store them in a map for now: | |
319 | boost::regex ipp_mask("boost_(?:(has)|no).*\\.ipp"); | |
320 | boost::smatch ipp_match; | |
321 | fs::directory_iterator i(config_path), j; | |
322 | std::map<fs::path, bool> files_to_process; | |
323 | while(i != j) | |
324 | { | |
325 | if(boost::regex_match(i->path().leaf().string(), ipp_match, ipp_mask)) | |
326 | { | |
327 | files_to_process[*i] = ipp_match[1].matched; | |
328 | } | |
329 | ++i; | |
330 | } | |
331 | // Enumerate the files and process them, by defering this until now | |
332 | // the results are always alphabetized which reduces churn in the | |
333 | // generated files. | |
334 | for(std::map<fs::path, bool>::const_iterator pos = files_to_process.begin(); pos != files_to_process.end(); ++pos) | |
335 | { | |
336 | process_ipp_file(pos->first, pos->second); | |
337 | } | |
338 | write_config_test(); | |
339 | write_jamfile_v2(); | |
340 | write_config_info(); | |
341 | write_build_tests(); | |
342 | write_build_check_jamfile(); | |
343 | return 0; | |
344 | } | |
345 |