1 /**
2  * Run the C preprocessor on a C source file.
3  *
4  * Specification: C11
5  *
6  * Copyright:   Copyright (C) 2022-2023 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cpreprocess.d, _cpreprocess.d)
10  * Documentation:  https://dlang.org/phobos/dmd_cpreprocess.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cpreprocess.d
12  */
13 
14 module dmd.cpreprocess;
15 
16 import core.stdc.stdio;
17 import core.stdc.stdlib;
18 import core.stdc.string;
19 
20 import dmd.astenums;
21 import dmd.errors;
22 import dmd.globals;
23 import dmd.link;
24 import dmd.location;
25 import dmd.target;
26 import dmd.vsoptions;
27 
28 import dmd.common.outbuffer;
29 
30 import dmd.root.array;
31 import dmd.root.filename;
32 import dmd.root.rmem;
33 import dmd.root.rootobject;
34 import dmd.root.string;
35 
36 // Use default for other versions
37 version (Posix)   version = runPreprocessor;
38 version (Windows) version = runPreprocessor;
39 
40 
41 /***************************************
42  * Preprocess C file.
43  * Params:
44  *      csrcfile = C file to be preprocessed, with .c or .h extension
45  *      loc = The source location where preprocess is requested from
46  *      ifile = set to true if an output file was written
47  *      defines = buffer to append any `#define` and `#undef` lines encountered to
48  * Result:
49  *      filename of output
50  */
51 extern (C++)
52 FileName preprocess(FileName csrcfile, ref const Loc loc, out bool ifile, OutBuffer* defines)
53 {
54     /* Look for "importc.h" by searching along import path.
55      * It should be in the same place as "object.d"
56      */
57     const(char)* importc_h;
58 
59     foreach (entry; (global.path ? (*global.path)[] : null))
60     {
61         auto f = FileName.combine(entry, "importc.h");
62         if (FileName.exists(f) == 1)
63         {
64             importc_h = f;
65             break;
66         }
67         FileName.free(f);
68     }
69 
70     if (importc_h)
71     {
72         if (global.params.verbose)
73             message("include   %s", importc_h);
74     }
75     else
76     {
77         error(loc, "cannot find \"importc.h\" along import path");
78         fatal();
79     }
80 
81     //printf("preprocess %s\n", csrcfile.toChars());
82     version (runPreprocessor)
83     {
84         /*
85            To get sppn.exe: http://ftp.digitalmars.com/sppn.zip
86            To get the dmc C headers, dmc will need to be installed:
87            http://ftp.digitalmars.com/Digital_Mars_C++/Patch/dm857c.zip
88          */
89         const name = FileName.name(csrcfile.toString());
90         const ext = FileName.ext(name);
91         assert(ext);
92         const ifilename = FileName.addExt(name[0 .. name.length - (ext.length + 1)], i_ext);
93         const command = global.params.cpp ? toDString(global.params.cpp) : cppCommand();
94         auto status = runPreprocessor(command, csrcfile.toString(), importc_h, global.params.cppswitches, ifilename, defines);
95         if (status)
96         {
97             error(loc, "C preprocess command %.*s failed for file %s, exit status %d\n",
98                 cast(int)command.length, command.ptr, csrcfile.toChars(), status);
99             fatal();
100         }
101         //printf("C preprocess succeeded %s\n", ifilename.ptr);
102         ifile = true;
103         return FileName(ifilename);
104     }
105     else
106         return csrcfile;        // no-op
107 }
108 
109 private const(char)[] cppCommand()
110 {
111     if (auto p = getenv("CPPCMD"))
112         return toDString(p);
113 
114     version (Windows)
115     {
116         if (target.objectFormat() == Target.ObjectFormat.coff)
117         {
118             VSOptions vsopt;
119             vsopt.initialize();
120             auto path = vsopt.compilerPath(target.is64bit);
121             return toDString(path);
122         }
123         if (target.objectFormat() == Target.ObjectFormat.omf)
124         {
125             return "sppn.exe";
126         }
127         // Perhaps we are cross-compiling.
128         return "cpp";
129     }
130     else version (OpenBSD)
131     {
132         // On OpenBSD, we need to use the actual binary /usr/libexec/cpp
133         // rather than the shell script wrapper /usr/bin/cpp ...
134         // Turns out the shell script doesn't really understand -o
135         return "/usr/libexec/cpp";
136     }
137     else version (OSX)
138     {
139         return "clang";
140     }
141     else
142     {
143         return "cpp";
144     }
145 }