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