Skip to content

Commit 94df984

Browse files
committed
Init
1 parent 5593ab4 commit 94df984

File tree

2 files changed

+340
-0
lines changed

2 files changed

+340
-0
lines changed

Header_File_constructor.cpp

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
// Header_File_constructor.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
2+
//
3+
4+
#include <iostream>
5+
#include <fstream>
6+
#include <string>
7+
#include <vector>
8+
#include <regex>
9+
#include <filesystem>
10+
#include <map>
11+
12+
std::regex include_reg("^#[ \t]*include[ \t]+\"([^\"]+)\"[ \t]*");
13+
std::regex define_with_value_reg("^#[ \t]*define[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]+(.*)[ \t]*");
14+
std::regex define_reg("^#[ \t]*define[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*");
15+
std::regex undef_reg("^#[ \t]*undef[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*");
16+
std::regex ifndef_reg("^#[ \t]*ifndef[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*");
17+
//#if !defined(
18+
std::regex ifndef_reg2("^#[ \t]*if[ \t]+!defined[ \t]*\\([ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*\\)[ \t]*");
19+
std::regex endif_reg("^#[ \t]*endif[ \t]*");
20+
21+
std::map<std::string, std::string> define_map;
22+
23+
class NullStream: public std::ostream {
24+
class NullBuffer: public std::streambuf {
25+
public:
26+
int overflow(int c) { return c; }
27+
} m_nb;
28+
29+
public:
30+
NullStream():
31+
std::ostream(&m_nb) {}
32+
};
33+
NullStream nullstream;
34+
35+
template<class char_t>
36+
inline std::basic_string<char_t>& replace_all(std::basic_string<char_t>& a, const std::basic_string_view<char_t> b, const std::basic_string_view<char_t> c) {
37+
auto i = a.find(b);
38+
while(i != std::basic_string<char_t>::npos) {
39+
a.replace(i, b.size(), c);
40+
i = a.find(b, i + c.size());
41+
}
42+
return a;
43+
};
44+
template<class char_t, class T1, class T2>
45+
inline std::basic_string<char_t>& replace_all(std::basic_string<char_t>& a, T1&& b, T2&& c) {
46+
return replace_all(a, std::basic_string_view<char_t>(b), std::basic_string_view<char_t>(c));
47+
};
48+
49+
50+
namespace arg_info {
51+
std::filesystem::path in_path;
52+
std::filesystem::path in_path_dir;
53+
std::filesystem::path out_path;
54+
bool is_full_mode = false; //-f or --full : Even under folder handling each file is guaranteed to be included individually without error
55+
bool open_help = false; //-h or --help : Display help
56+
std::string relocate_path; //-r or --relocate : Relocate the input file path in "#line" to the specified path
57+
bool relocate_path_was_an_url = false;
58+
} // namespace arg_info
59+
60+
//路径转义为合法的C++字符串
61+
inline std::string path_to_string(std::filesystem::path path) {
62+
std::string aret;
63+
if(!arg_info::relocate_path.empty()) {
64+
//get relative path of path to arg_info::in_path_dir
65+
auto relative_path = std::filesystem::relative(path, arg_info::in_path_dir);
66+
aret = arg_info::relocate_path + relative_path.string();
67+
}
68+
else
69+
aret = path.string();
70+
if(arg_info::relocate_path_was_an_url)
71+
replace_all(aret, "\\", "/");
72+
replace_all(aret, "//", "/");
73+
replace_all(aret, "\\\\", "\\");
74+
if(arg_info::relocate_path_was_an_url) {
75+
replace_all(aret, ":/", "://");
76+
replace_all(aret, " ", "%20");
77+
}
78+
return replace_all(aret, "\\", "\\\\");
79+
}
80+
81+
void process_file(std::filesystem::path in_file, std::istream& in, std::ostream& out, std::string line_begin, std::filesystem::path include_path, std::filesystem::path root_path_for_skip) {
82+
std::string line;
83+
size_t line_num = 0;
84+
//write #line
85+
out << line_begin << "#line 1 \"" << path_to_string(in_file) << "\"" << std::endl;
86+
//process file
87+
while(std::getline(in, line)) {
88+
line_num++;
89+
//get line begin of this line
90+
std::string line_begin_of_this_line = line_begin;
91+
{
92+
auto pos = line.find_first_not_of(" \t");
93+
if(pos != std::string::npos) {
94+
line_begin_of_this_line += line.substr(0, pos);
95+
line = line.substr(pos);
96+
}
97+
}
98+
std::smatch result;
99+
//match include
100+
if(std::regex_search(line, result, include_reg)) {
101+
std::string file_name = result[1];
102+
std::filesystem::path file_base_path = file_name;
103+
std::filesystem::path file_path = include_path / file_base_path;
104+
//get content after "#include"
105+
std::string content_after_include = line.substr(result.position() + result.length());
106+
//content_after_include must be empty or end with "//"
107+
if(content_after_include.empty() || content_after_include.find("//") == 0) {
108+
std::ifstream include_file(file_path);
109+
if(include_file.is_open()) {
110+
//if file_path's parent is root_path_for_skip, use NullStream
111+
//not skip the processing of the contents of the file as it may have definitions.
112+
if(!arg_info::is_full_mode && std::filesystem::equivalent(file_path.parent_path(), root_path_for_skip)) {
113+
out << line_begin_of_this_line << "#include \"" << file_path.filename().string() << "\"" << content_after_include << std::endl;
114+
process_file(file_path, include_file, nullstream, line_begin_of_this_line, file_path.parent_path(), root_path_for_skip);
115+
}
116+
else {
117+
if(!content_after_include.empty()) //has comment
118+
out << line_begin_of_this_line << content_after_include << std::endl;
119+
process_file(file_path, include_file, out, line_begin_of_this_line, file_path.parent_path(), root_path_for_skip);
120+
//write #line to reset
121+
out << line_begin_of_this_line << "#line " << line_num << " \"" << path_to_string(in_file) << "\"" << std::endl;
122+
}
123+
include_file.close();
124+
}
125+
else {
126+
out << line_begin_of_this_line << line << std::endl;
127+
std::cerr << "can't open file: " << file_path << std::endl;
128+
}
129+
}
130+
else {
131+
out << line_begin_of_this_line << line << std::endl;
132+
}
133+
}
134+
//match define
135+
else if(std::regex_search(line, result, define_reg)) {
136+
std::string define_name = result[1];
137+
define_map[define_name] = "";
138+
out << line_begin_of_this_line << line << std::endl;
139+
}
140+
//match define_with_value
141+
else if(std::regex_search(line, result, define_with_value_reg)) {
142+
std::string define_name = result[1];
143+
std::string define_value = result[2];
144+
//get content after "#define"
145+
std::string content_after_define = line.substr(result.position() + result.length());
146+
//content_after_define must be empty or end with "//"
147+
if(content_after_define.empty() || content_after_define.find("//") == 0) {
148+
define_map[define_name] = define_value;
149+
}
150+
out << line_begin_of_this_line << line << std::endl;
151+
}
152+
//match undef
153+
else if(std::regex_search(line, result, undef_reg)) {
154+
std::string define_name = result[1];
155+
//get content after "#undef"
156+
std::string content_after_undef = line.substr(result.position() + result.length());
157+
//content_after_undef must be empty or end with "//"
158+
if(content_after_undef.empty() || content_after_undef.find("//") == 0) {
159+
define_map.erase(define_name);
160+
}
161+
out << line_begin_of_this_line << line << std::endl;
162+
}
163+
//match ifndef
164+
else if(std::regex_search(line, result, ifndef_reg)) {
165+
ifndef_reg_process:
166+
std::string define_name = result[1];
167+
//get content after "#ifndef"
168+
std::string content_after_ifndef = line.substr(result.position() + result.length());
169+
//content_after_ifndef must be empty or end with "//"
170+
if(content_after_ifndef.empty() || content_after_ifndef.find("//") == 0) {
171+
if(define_map.find(define_name) != define_map.end()) {
172+
//skip this line and all lines until #endif
173+
while(std::getline(in, line)) {
174+
line_num++;
175+
if(std::regex_search(line, result, endif_reg)) {
176+
//get content after "#endif"
177+
std::string content_after_endif = line.substr(result.position() + result.length());
178+
//content_after_endif must be empty or end with "//"
179+
if(content_after_endif.empty() || content_after_endif.find("//") == 0) {
180+
//write #line
181+
out << line_begin_of_this_line << "#line " << line_num << " \"" << path_to_string(in_file) << "\"" << std::endl;
182+
break;
183+
}
184+
}
185+
}
186+
}
187+
else {
188+
out << line_begin_of_this_line << line << std::endl;
189+
}
190+
}
191+
else {
192+
out << line_begin_of_this_line << line << std::endl;
193+
}
194+
}
195+
//match ifndef_reg2
196+
else if(std::regex_search(line, result, ifndef_reg2)) {
197+
goto ifndef_reg_process;
198+
}
199+
else {
200+
if(line.empty())
201+
out << std::endl;
202+
else
203+
out << line_begin_of_this_line << line << std::endl;
204+
}
205+
}
206+
}
207+
208+
void process_file(std::string in_file_name, std::string out_file_name, std::filesystem::path root_path_for_skip) {
209+
std::cout << "process file: " << in_file_name << std::endl;
210+
std::ifstream in_file(in_file_name);
211+
std::ofstream out_file(out_file_name, std::ios_base::binary);
212+
std::filesystem::path include_path = std::filesystem::path(in_file_name).parent_path();
213+
if(in_file.is_open() && out_file.is_open()) {
214+
process_file(in_file_name, in_file, out_file, "", include_path, root_path_for_skip);
215+
in_file.close();
216+
out_file.close();
217+
}
218+
else {
219+
std::cerr << "can't open file" << std::endl;
220+
}
221+
for(auto& [key, value]: define_map) {
222+
std::cout << "warning: define " << key << " is not undef" << std::endl;
223+
}
224+
define_map.clear();
225+
std::cout << "process file: " << in_file_name << " done\n\n"
226+
<< std::endl;
227+
}
228+
229+
230+
void print_help() {
231+
std::cout << "Usage: Header_File_constructor [options] in_file out_file" << std::endl;
232+
std::cout << "Options:" << std::endl;
233+
std::cout << " -f, --full" << std::endl;
234+
std::cout << " Even under folder handling each file is guaranteed to be included individually without error" << std::endl;
235+
std::cout << " -h, --help" << std::endl;
236+
std::cout << " Display help" << std::endl;
237+
std::cout << " -r, --relocate" << std::endl;
238+
std::cout << " Relocate the input file path in \"#line\" to the specified path" << std::endl;
239+
std::cout << "if in_file is a directory, out_file must be a directory or not exist," << std::endl;
240+
std::cout << "and all superficial files in in_file will be processed." << std::endl;
241+
}
242+
243+
int main(size_t argc, char* _argv[]) {
244+
//build argv
245+
std::vector<std::string> argv;
246+
for(size_t i = 0; i < argc; i++) {
247+
argv.push_back(_argv[i]);
248+
}
249+
//process argv
250+
for(size_t i = 1; i < argv.size(); i++) {
251+
std::string arg = argv[i];
252+
if(arg == "-f" || arg == "--full") {
253+
arg_info::is_full_mode = true;
254+
}
255+
else if(arg == "-h" || arg == "--help") {
256+
arg_info::open_help = true;
257+
}
258+
else if(arg == "-r" || arg == "--relocate") {
259+
if(i + 1 < argv.size()) {
260+
arg_info::relocate_path = argv[i + 1];
261+
//if arg_info::relocate_path is an url, add a "/" at the end and replace all "\" with "/"
262+
if(arg_info::relocate_path.find("://") != std::string::npos) {
263+
arg_info::relocate_path_was_an_url = true;
264+
if(arg_info::relocate_path.back() != '/')
265+
arg_info::relocate_path += '/';
266+
}
267+
i++;
268+
}
269+
else {
270+
std::cerr << "error: -r or --relocate must be followed by a path" << std::endl;
271+
return 1;
272+
}
273+
}
274+
else {
275+
if(arg_info::in_path.empty()) {
276+
arg_info::in_path = arg;
277+
}
278+
else if(arg_info::out_path.empty()) {
279+
arg_info::out_path = arg;
280+
}
281+
else {
282+
std::cerr << "error: too many arguments" << std::endl;
283+
return 1;
284+
}
285+
}
286+
}
287+
if(arg_info::out_path.empty())
288+
arg_info::out_path = arg_info::in_path / ".out";
289+
if(arg_info::open_help || arg_info::in_path.empty()) {
290+
print_help();
291+
return 0;
292+
}
293+
arg_info::in_path = std::filesystem::absolute(arg_info::in_path);
294+
arg_info::out_path = std::filesystem::absolute(arg_info::out_path);
295+
if(std::filesystem::is_regular_file(arg_info::in_path)) {
296+
arg_info::is_full_mode = true;
297+
std::filesystem::create_directories(arg_info::out_path.parent_path());
298+
arg_info::in_path_dir = arg_info::in_path.parent_path();
299+
process_file(arg_info::in_path.string(), arg_info::out_path.string(), arg_info::in_path_dir);
300+
}
301+
else if(std::filesystem::is_directory(arg_info::in_path)) {
302+
std::filesystem::create_directories(arg_info::out_path);
303+
arg_info::in_path_dir = arg_info::in_path;
304+
//process just superficial files
305+
for(auto& p: std::filesystem::directory_iterator(arg_info::in_path)) {
306+
if(std::filesystem::is_regular_file(p)) {
307+
std::filesystem::path in_file_path = p;
308+
std::filesystem::path out_file_path = arg_info::out_path / in_file_path.filename();
309+
process_file(in_file_path.string(), out_file_path.string(), arg_info::in_path_dir);
310+
}
311+
}
312+
}
313+
else {
314+
std::cerr << "error: " << arg_info::in_path << " is not a file or directory" << std::endl;
315+
return 1;
316+
}
317+
return 0;
318+
}

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Header_File_constructor
2+
3+
Intelligent pre-processing of headers to generate single file headers without system header content
4+
5+
## usage
6+
7+
```text
8+
Header_File_constructor [options] in_file out_file
9+
Options:
10+
-f, --full
11+
Even under folder handling each file is guaranteed to be included individually without error
12+
-h, --help
13+
Display help
14+
-r, --relocate
15+
Relocate the input file path in "#line" to the specified path
16+
if in_file is a directory, out_file must be a directory or not exist,
17+
and all superficial files in in_file will be processed.
18+
```
19+
20+
## example
21+
22+
[online_cpp_header_files](https://github.com/ELC-lang/online_cpp_header_files/tree/master/files) form [`elc`](https://github.com/ELC-lang/ELC/blob/master/parts/header_file/files/elc)

0 commit comments

Comments
 (0)