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)
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.
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>
23 namespace fs
= boost::filesystem
;
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"
33 "// See http://www.boost.org/libs/config for the most recent version."
34 "//\n// Revision $Id$\n//\n");
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
;
44 std::set
<std::string
> feature_list
;
46 void write_config_info()
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
));
55 for(std::set
<std::string
>::const_iterator
i(macro_list
.begin()), j(macro_list
.end());
59 ss
<< " PRINT_MACRO(" << *i
<< ");\n";
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");
65 if(boost::regex_search(file_text
, what
, re
))
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");
76 void write_config_test()
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";
94 void write_jamfile_v2()
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
;
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
,
127 if(!fs::exists(file
))
129 std::cout
<< "Writing test file " << file
.string() << std::endl
;
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
;
139 ofs
<< "// This file should compile, if it does not then\n"
140 "// " << macro_name
<< " should ";
143 ofs
<< "be defined.\n";
147 ofs
<< "// This file should not compile, if it does then\n"
148 "// " << macro_name
<< " should ";
151 ofs
<< "be defined.\n";
153 ofs
<< "// See file " << header_file
<< " for details\n\n";
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"
161 static const boost::regex
tr1_exp("BOOST_HAS_TR1.*");
163 ofs
<< "#include <boost/config.hpp>\n";
165 ofs
<< "#include \"test.hpp\"\n\n"
167 if(positive_test
!= expect_success
)
169 ofs
<< "def " << macro_name
<<
170 "\n#include \"" << header_file
<<
173 ofs
<< "namespace " << namespace_name
<< " = empty_boost;\n";
175 ofs
<< "#error \"this file should not compile\"\n";
178 ofs
<< "int main( int, char *[] )\n{\n return " << namespace_name
<< "::test();\n}\n\n";
182 std::cout
<< "Skipping existing test file " << file
.string() << std::endl
;
186 void write_build_tests()
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
;
192 ofs
<< "#include <boost/config.hpp>\n\n";
193 ofs
<< build_config_test
.str() << std::endl
;
194 ofs
<< "int main( int, char *[] )\n{\n" << " return 0;\n}\n\n";
197 void write_build_check_jamfile()
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"
211 ofs
<< build_config_jamfile
.str() << std::endl
;
214 void process_ipp_file(const fs::path
& file
, bool positive_test
)
216 std::cout
<< "Info: Scanning file: " << file
.string() << std::endl
;
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
;
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
));
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
))
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
);
238 if(macro_name
.empty())
240 std::cout
<< "Error: no macro definition found in " << file
.string();
244 std::cout
<< "Info: Macroname: " << macro_name
<< std::endl
;
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);
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
;
261 *pout
<< "def " << macro_name
262 << "\n#include \"" << file
.leaf().string() << "\"\n#else\nnamespace "
263 << namespace_name
<< " = empty_boost;\n#endif\n";
265 config_test2
<< " if(0 != " << namespace_name
<< "::test())\n"
267 " std::cerr << \"Failed test for " << macro_name
<< " at: \" << __FILE__ << \":\" << __LINE__ << std::endl;\n"
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";
276 jamfile_v2
<< "test-suite \"" << macro_name
<< "\" : \n"
277 "[ run ../" << positive_file
.leaf().string() << " ]\n"
278 "[ compile-fail ../" << negative_file
.leaf().string() << " ] ;\n";
280 // Generate data for the Build-checks test file:
281 build_config_test
<< "#ifdef TEST_" << macro_name
<< std::endl
;
284 build_config_test
<< "# ifndef " << macro_name
<< "\n# error \"Feature macro " << macro_name
<< " is not defined.\"\n# endif\n";
288 build_config_test
<< "# ifdef " << macro_name
<< "\n# error \"Defect macro " << macro_name
<< " is defined.\"\n# endif\n";
290 build_config_test
<< "#endif\n";
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");
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
);
300 int cpp_main(int argc
, char* argv
[])
303 // get the boost path to begin with:
308 config_path
= p
/ "libs" / "config" / "test" ;
313 fs::path
p(__FILE__
);
314 config_path
= p
.branch_path().branch_path() / "test";
316 std::cout
<< "Info: Boost.Config test path set as: " << config_path
.string() << std::endl
;
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
;
325 if(boost::regex_match(i
->path().leaf().string(), ipp_match
, ipp_mask
))
327 files_to_process
[*i
] = ipp_match
[1].matched
;
331 // Enumerate the files and process them, by defering this until now
332 // the results are always alphabetized which reduces churn in the
334 for(std::map
<fs::path
, bool>::const_iterator pos
= files_to_process
.begin(); pos
!= files_to_process
.end(); ++pos
)
336 process_ipp_file(pos
->first
, pos
->second
);
342 write_build_check_jamfile();