| 
									
										
										
										
											2020-09-08 15:53:08 +02:00
										 |  |  | #!/usr/bin/env python3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import datetime | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from scriptCommon import catchPath | 
					
						
							|  |  |  | from releaseCommon import Version | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | root_path = os.path.join(catchPath, 'src') | 
					
						
							|  |  |  | starting_header = os.path.join(root_path, 'catch2', 'catch_all.hpp') | 
					
						
							|  |  |  | output_header = os.path.join(catchPath, 'extras', 'catch_amalgamated.hpp') | 
					
						
							|  |  |  | output_cpp = os.path.join(catchPath, 'extras', 'catch_amalgamated.cpp') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # These are the copyright comments in each file, we want to ignore them | 
					
						
							|  |  |  | copyright_lines = [ | 
					
						
							|  |  |  | '//              Copyright Catch2 Authors\n', | 
					
						
							|  |  |  | '// Distributed under the Boost Software License, Version 1.0.\n', | 
					
						
							|  |  |  | '//   (See accompanying file LICENSE_1_0.txt or copy at\n', | 
					
						
							|  |  |  | '//        https://www.boost.org/LICENSE_1_0.txt)\n', | 
					
						
							|  |  |  | '// SPDX-License-Identifier: BSL-1.0\n', | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # The header of the amalgamated file: copyright information + explanation | 
					
						
							|  |  |  | # what this file is. | 
					
						
							|  |  |  | file_header = '''\
 | 
					
						
							|  |  |  | //              Copyright Catch2 Authors | 
					
						
							|  |  |  | // Distributed under the Boost Software License, Version 1.0. | 
					
						
							|  |  |  | //   (See accompanying file LICENSE_1_0.txt or copy at | 
					
						
							|  |  |  | //        https://www.boost.org/LICENSE_1_0.txt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SPDX-License-Identifier: BSL-1.0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //  Catch v{version_string} | 
					
						
							|  |  |  | //  Generated: {generation_time} | 
					
						
							|  |  |  | //  ---------------------------------------------------------- | 
					
						
							|  |  |  | //  This file is an amalgamation of multiple different files. | 
					
						
							|  |  |  | //  You probably shouldn't edit it directly. | 
					
						
							|  |  |  | //  ---------------------------------------------------------- | 
					
						
							|  |  |  | '''
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Returns file header with proper version string and generation time | 
					
						
							|  |  |  | def formatted_file_header(version): | 
					
						
							|  |  |  |     return file_header.format(version_string=version.getVersionString(), | 
					
						
							|  |  |  |                               generation_time=datetime.datetime.now()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Which headers were already concatenated (and thus should not be | 
					
						
							|  |  |  | # processed again) | 
					
						
							|  |  |  | concatenated_headers = set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | internal_include_parser = re.compile(r'\s*#include <(catch2/.*)>.*') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def concatenate_file(out, filename: str, expand_headers: bool) -> int: | 
					
						
							|  |  |  |     # Gathers statistics on how many headers were expanded | 
					
						
							|  |  |  |     concatenated = 1 | 
					
						
							|  |  |  |     with open(filename, mode='r', encoding='utf-8') as input: | 
					
						
							|  |  |  |         for line in input: | 
					
						
							|  |  |  |             if line in copyright_lines: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             m = internal_include_parser.match(line) | 
					
						
							|  |  |  |             # anything that isn't a Catch2 header can just be copied to | 
					
						
							|  |  |  |             # the resulting file | 
					
						
							|  |  |  |             if not m: | 
					
						
							|  |  |  |                 out.write(line) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # TBD: We can also strip out include guards from our own | 
					
						
							|  |  |  |             # headers, but it wasn't worth the time at the time of writing | 
					
						
							|  |  |  |             # this script. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # We do not want to expand headers for the cpp file | 
					
						
							|  |  |  |             # amalgamation but neither do we want to copy them to output | 
					
						
							|  |  |  |             if not expand_headers: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             next_header = m.group(1) | 
					
						
							|  |  |  |             # We have to avoid re-expanding the same header over and | 
					
						
							|  |  |  |             # over again, or the header will end up with couple | 
					
						
							|  |  |  |             # hundred thousands lines (~300k as of preview3 :-) ) | 
					
						
							|  |  |  |             if next_header in concatenated_headers: | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2022-02-02 15:36:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Skip including the auto-generated user config file, | 
					
						
							|  |  |  |             # because it has not been generated yet at this point. | 
					
						
							|  |  |  |             # The code around it should be written so that just not including | 
					
						
							|  |  |  |             # it is equivalent with all-default user configuration. | 
					
						
							|  |  |  |             if next_header == 'catch2/catch_user_config.hpp': | 
					
						
							|  |  |  |                 concatenated_headers.add(next_header) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-08 15:53:08 +02:00
										 |  |  |             concatenated_headers.add(next_header) | 
					
						
							|  |  |  |             concatenated += concatenate_file(out, os.path.join(root_path, next_header), expand_headers) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return concatenated | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_header(): | 
					
						
							|  |  |  |     with open(output_header, mode='w', encoding='utf-8') as header: | 
					
						
							|  |  |  |         header.write(formatted_file_header(Version())) | 
					
						
							|  |  |  |         header.write('#ifndef CATCH_AMALGAMATED_HPP_INCLUDED\n') | 
					
						
							|  |  |  |         header.write('#define CATCH_AMALGAMATED_HPP_INCLUDED\n') | 
					
						
							|  |  |  |         print('Concatenated {} headers'.format(concatenate_file(header, starting_header, True))) | 
					
						
							|  |  |  |         header.write('#endif // CATCH_AMALGAMATED_HPP_INCLUDED\n') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_cpp(): | 
					
						
							|  |  |  |     from glob import glob | 
					
						
							|  |  |  |     cpp_files = sorted(glob(os.path.join(root_path, 'catch2', '**/*.cpp'), recursive=True)) | 
					
						
							|  |  |  |     with open(output_cpp, mode='w', encoding='utf-8') as cpp: | 
					
						
							|  |  |  |         cpp.write(formatted_file_header(Version())) | 
					
						
							|  |  |  |         cpp.write('\n#include "catch_amalgamated.hpp"\n') | 
					
						
							|  |  |  |         for file in cpp_files: | 
					
						
							|  |  |  |             concatenate_file(cpp, file, False) | 
					
						
							|  |  |  |     print('Concatenated {} cpp files'.format(len(cpp_files))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-02 11:48:50 +01:00
										 |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     generate_header() | 
					
						
							|  |  |  |     generate_cpp() | 
					
						
							| 
									
										
										
										
											2020-09-08 15:53:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Notes: | 
					
						
							|  |  |  | # * For .cpp files, internal includes have to be stripped and rewritten | 
					
						
							|  |  |  | # * for .hpp files, internal includes have to be resolved and included | 
					
						
							|  |  |  | # * The .cpp file needs to start with `#include "catch_amalgamated.hpp" | 
					
						
							|  |  |  | # * include guards can be left/stripped, doesn't matter | 
					
						
							|  |  |  | # * *.cpp files should be included sorted, to minimize diffs between versions | 
					
						
							|  |  |  | # * *.hpp files should also be somehow sorted -> use catch_all.hpp as the | 
					
						
							|  |  |  | # *       entrypoint | 
					
						
							|  |  |  | # * allow disabling main in the .cpp amalgamation |