summaryrefslogtreecommitdiff
path: root/cpp/src/Slice/Preprocessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/Slice/Preprocessor.cpp')
-rw-r--r--cpp/src/Slice/Preprocessor.cpp624
1 files changed, 624 insertions, 0 deletions
diff --git a/cpp/src/Slice/Preprocessor.cpp b/cpp/src/Slice/Preprocessor.cpp
new file mode 100644
index 00000000000..6f75020a71d
--- /dev/null
+++ b/cpp/src/Slice/Preprocessor.cpp
@@ -0,0 +1,624 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <IceUtil/DisableWarnings.h>
+#include <Slice/Preprocessor.h>
+#include <Slice/Util.h>
+#include <IceUtil/StringUtil.h>
+#include <IceUtil/FileUtil.h>
+#include <IceUtil/UUID.h>
+#include <IceUtil/Unicode.h>
+#include <algorithm>
+#include <fstream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <cstring>
+
+#ifndef _WIN32
+# include <sys/wait.h>
+#endif
+
+using namespace std;
+using namespace Slice;
+
+//
+// mcpp defines
+//
+namespace Slice
+{
+
+enum Outdest
+{
+ Out=0, Err=1, Dbg=2, Num_Outdest=3
+};
+
+}
+
+extern "C" int mcpp_lib_main(int argc, char** argv);
+extern "C" void mcpp_use_mem_buffers(int tf);
+extern "C" char* mcpp_get_mem_buffer(Outdest od);
+
+Slice::PreprocessorPtr
+Slice::Preprocessor::create(const string& path, const string& fileName, const vector<string>& args)
+{
+ return new Preprocessor(path, fileName, args);
+}
+
+Slice::Preprocessor::Preprocessor(const string& path, const string& fileName, const vector<string>& args) :
+ _path(path),
+ _fileName(fullPath(fileName)),
+ _shortFileName(fileName),
+ _args(args),
+ _cppHandle(0)
+{
+}
+
+Slice::Preprocessor::~Preprocessor()
+{
+ close();
+}
+
+string
+Slice::Preprocessor::getBaseName()
+{
+ string base(_fileName);
+ string suffix;
+ string::size_type pos = base.rfind('.');
+ if(pos != string::npos)
+ {
+ base.erase(pos);
+ }
+ return base;
+}
+
+string
+Slice::Preprocessor::addQuotes(const string& arg)
+{
+ //
+ // Add quotes around the given argument to ensure that arguments
+ // with spaces will be preserved as a single argument. We also
+ // escape the "\" character to ensure that we don't end up with a
+ // \" at the end of the string.
+ //
+ return "\"" + IceUtilInternal::escapeString(arg, "\\") + "\"";
+}
+
+string
+Slice::Preprocessor::normalizeIncludePath(const string& path)
+{
+ string result = path;
+
+#ifdef _WIN32
+ //
+ // MCPP does not handle "-IC:/" well as an include path.
+ //
+ if(path.size() != 3 || !(path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z') ||
+ path[1] != ':' || path[2] != '\\')
+#endif
+ {
+ replace(result.begin(), result.end(), '\\', '/');
+ }
+
+ string::size_type startReplace = 0;
+#ifdef _WIN32
+ //
+ // For UNC paths we need to ensure they are in the format that is
+ // used by MCPP. IE. "//MACHINE/PATH"
+ //
+ if(result.find("//") == 0)
+ {
+ startReplace = 2;
+ }
+#endif
+ string::size_type pos;
+ while((pos = result.find("//", startReplace)) != string::npos)
+ {
+ result.replace(pos, 2, "/");
+ }
+
+ if(result == "/" || (result.size() == 3 && IceUtilInternal::isAlpha(result[0]) && result[1] == ':' &&
+ result[2] == '/'))
+ {
+ return result;
+ }
+
+ if(result.size() > 1 && result[result.size() - 1] == '/')
+ {
+ result.erase(result.size() - 1);
+ }
+
+ return result;
+}
+
+FILE*
+Slice::Preprocessor::preprocess(bool keepComments)
+{
+ if(!checkInputFile())
+ {
+ return 0;
+ }
+
+ //
+ // Build arguments list.
+ //
+ vector<string> args = _args;
+ if(keepComments)
+ {
+ args.push_back("-C");
+ }
+ args.push_back("-e");
+ args.push_back("en_us.utf8");
+ args.push_back(_fileName);
+
+ const char** argv = new const char*[args.size() + 1];
+ argv[0] = "mcpp";
+ for(unsigned int i = 0; i < args.size(); ++i)
+ {
+ argv[i + 1] = args[i].c_str();
+ }
+
+ //
+ // Call mcpp using memory buffer.
+ //
+ mcpp_use_mem_buffers(1);
+ int status = mcpp_lib_main(static_cast<int>(args.size()) + 1, const_cast<char**>(argv));
+ delete[] argv;
+
+ //
+ // Display any errors.
+ //
+ char* err = mcpp_get_mem_buffer(Err);
+ if(err)
+ {
+ vector<string> messages = filterMcppWarnings(err);
+ for(vector<string>::const_iterator i = messages.begin(); i != messages.end(); ++i)
+ {
+ emitRaw(i->c_str());
+
+ //
+ // MCPP FIX: mcpp does not always return non-zero exit status when there is an error.
+ //
+ if(i->find("error:") != string::npos)
+ {
+ status = 1;
+ }
+ }
+ }
+
+ if(status == 0)
+ {
+ //
+ // Write output to temporary file.
+ //
+ char* buf = mcpp_get_mem_buffer(Out);
+
+ //
+ // First try to open temporay file in tmp directory.
+ //
+#ifdef _WIN32
+ wchar_t* name = _wtempnam(NULL, L".preprocess");
+ if(name)
+ {
+ _cppFile = IceUtil::wstringToString(name);
+ free(name);
+ _cppHandle = IceUtilInternal::fopen(_cppFile, "w+");
+ }
+#else
+ _cppHandle = tmpfile();
+#endif
+
+ //
+ // If that fails try to open file in current directory.
+ //
+ if(_cppHandle == 0)
+ {
+ _cppFile = ".preprocess." + IceUtil::generateUUID();
+ _cppHandle = IceUtilInternal::fopen(_cppFile, "w+");
+ }
+
+ if(_cppHandle != 0)
+ {
+ if(buf)
+ {
+ ::fwrite(buf, strlen(buf), 1, _cppHandle);
+ }
+ ::rewind(_cppHandle);
+ }
+ else
+ {
+ ostream& os = getErrorStream();
+ os << _path << ": error: could not open temporary file: ";
+ os << _cppFile;
+ os << endl;
+ }
+ }
+
+ //
+ // Calling this again causes the memory buffers to be freed.
+ //
+ mcpp_use_mem_buffers(1);
+
+ return _cppHandle;
+}
+
+bool
+Slice::Preprocessor::printMakefileDependencies(Language lang, const vector<string>& includePaths,
+ const string& cppSourceExt, const string& pyPrefix)
+{
+ if(!checkInputFile())
+ {
+ return false;
+ }
+
+ //
+ // Build arguments list.
+ //
+ vector<string> args = _args;
+ args.push_back("-M");
+ args.push_back("-e");
+ args.push_back("en_us.utf8");
+ args.push_back(_fileName);
+
+ const char** argv = new const char*[args.size() + 1];
+ for(unsigned int i = 0; i < args.size(); ++i)
+ {
+ argv[i + 1] = args[i].c_str();
+ }
+
+ //
+ // Call mcpp using memory buffer.
+ //
+ mcpp_use_mem_buffers(1);
+ int status = mcpp_lib_main(static_cast<int>(args.size() + 1), const_cast<char**>(argv));
+ delete[] argv;
+
+ //
+ // Print errors to stderr.
+ //
+ char* err = mcpp_get_mem_buffer(Err);
+ if(err)
+ {
+ vector<string> messages = filterMcppWarnings(err);
+ for(vector<string>::const_iterator i = messages.begin(); i != messages.end(); ++i)
+ {
+ emitRaw(i->c_str());
+ }
+ }
+
+ if(status != 0)
+ {
+ //
+ // Calling this again causes the memory buffers to be freed.
+ //
+ mcpp_use_mem_buffers(1);
+ return false;
+ }
+
+ //
+ // Get mcpp output.
+ //
+ string unprocessed;
+ char* buf = mcpp_get_mem_buffer(Out);
+ if(buf)
+ {
+ unprocessed = string(buf);
+ }
+
+ //
+ // Calling this again causes the memory buffers to be freed.
+ //
+ mcpp_use_mem_buffers(1);
+
+ //
+ // We now need to massage the result to get the desired output.
+ // First make it a single line.
+ //
+ string::size_type pos;
+ while((pos = unprocessed.find("\\\n")) != string::npos)
+ {
+ unprocessed.replace(pos, 2, "");
+ }
+
+ //
+ // Get the main output file name.
+ //
+#ifdef _WIN32
+ string suffix = ".obj:";
+#else
+ string suffix = ".o:";
+#endif
+ pos = unprocessed.find(suffix) + suffix.size();
+ string result;
+ if(lang != JavaXML)
+ {
+ result = unprocessed.substr(0, pos);
+ }
+
+ vector<string> fullIncludePaths;
+ vector<string>::const_iterator p;
+ for(p = includePaths.begin(); p != includePaths.end(); ++p)
+ {
+ fullIncludePaths.push_back(fullPath(*p));
+ }
+
+ //
+ // Process each dependency.
+ //
+ string::size_type end;
+ while((end = unprocessed.find(".ice", pos)) != string::npos)
+ {
+ end += 4;
+ string file = IceUtilInternal::trim(unprocessed.substr(pos, end - pos));
+ if(IceUtilInternal::isAbsolutePath(file))
+ {
+ if(file == _fileName)
+ {
+ file = _shortFileName;
+ }
+ else
+ {
+ //
+ // Transform back full paths generated by mcpp to paths relative to the specified
+ // include paths.
+ //
+ string newFile = file;
+ for(vector<string>::const_iterator p = fullIncludePaths.begin(); p != fullIncludePaths.end(); ++p)
+ {
+ if(file.compare(0, p->length(), *p) == 0)
+ {
+ string s = includePaths[p - fullIncludePaths.begin()] + file.substr(p->length());
+ if(IceUtilInternal::isAbsolutePath(newFile) || s.size() < newFile.size())
+ {
+ newFile = s;
+ }
+ }
+ }
+ file = newFile;
+ }
+ }
+
+ if(lang == JavaXML)
+ {
+ if(result.size() == 0)
+ {
+ result = " <source name=\"" + file + "\">";
+ }
+ else
+ {
+ result += "\n <dependsOn name=\"" + file + "\"/>";
+ }
+ }
+ else
+ {
+ //
+ // Escape spaces in the file name.
+ //
+ string::size_type space = 0;
+ while((space = file.find(" ", space)) != string::npos)
+ {
+ file.replace(space, 1, "\\ ");
+ space += 2;
+ }
+
+ //
+ // Add to result
+ //
+ result += " \\\n " + file;
+ }
+ pos = end;
+ }
+ if(lang == JavaXML)
+ {
+ result += "\n </source>\n";
+ }
+ else
+ {
+ result += "\n";
+ }
+
+ /*
+ * Emit dependencies in any of the following formats, depending on the
+ * length of the filenames:
+ *
+ * x.o[bj]: /path/x.ice /path/y.ice
+ *
+ * x.o[bj]: /path/x.ice \
+ * /path/y.ice
+ *
+ * x.o[bj]: /path/x.ice /path/y.ice \
+ * /path/z.ice
+ *
+ * x.o[bj]: \
+ * /path/x.ice
+ *
+ * x.o[bj]: \
+ * /path/x.ice \
+ * /path/y.ice
+ *
+ * Spaces embedded within filenames are escaped with a backslash. Note that
+ * Windows filenames may contain colons.
+ *
+ */
+ switch(lang)
+ {
+ case CPlusPlus:
+ {
+ //
+ // Change .o[bj] suffix to the cpp source extension suffix.
+ //
+ string::size_type pos;
+ while((pos = result.find(suffix)) != string::npos)
+ {
+ result.replace(pos, suffix.size() - 1, "." + cppSourceExt);
+ }
+ break;
+ }
+ case JavaXML:
+ break;
+ case Java:
+ {
+ //
+ // We want to shift the files left one position, so that
+ // "x.cpp: x.ice y.ice" becomes "x.ice: y.ice".
+ //
+
+ //
+ // Remove the first file.
+ //
+ string::size_type start = result.find(suffix);
+ assert(start != string::npos);
+ start = result.find_first_not_of(" \t\r\n\\", start + suffix.size()); // Skip to beginning of next file.
+ assert(start != string::npos);
+ result.erase(0, start);
+
+ //
+ // Find end of next file.
+ //
+ string::size_type pos = 0;
+ while((pos = result.find_first_of(" :\t\r\n\\", pos + 1)) != string::npos)
+ {
+ if(result[pos] == ':')
+ {
+ result.insert(pos, 1, '\\'); // Escape colons.
+ ++pos;
+ }
+ else if(result[pos] == '\\') // Ignore escaped characters.
+ {
+ ++pos;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if(pos == string::npos)
+ {
+ result.append(":");
+ }
+ else
+ {
+ result.insert(pos, 1, ':');
+ }
+ break;
+ }
+ case CSharp:
+ {
+ //
+ // Change .o[bj] suffix to .cs suffix.
+ //
+ string::size_type pos;
+ while((pos = result.find(suffix)) != string::npos)
+ {
+ result.replace(pos, suffix.size() - 1, ".cs");
+ }
+ break;
+ }
+ case Python:
+ {
+ //
+ // Change .o[bj] suffix to .py suffix.
+ //
+ if(pyPrefix.size() != 0)
+ {
+ result = pyPrefix + result;
+ }
+ string::size_type pos;
+ while((pos = result.find(suffix)) != string::npos)
+ {
+ result.replace(pos, suffix.size() - 1, "_ice.py");
+ }
+ break;
+ }
+ case Ruby:
+ {
+ //
+ // Change .o[bj] suffix to .rb suffix.
+ //
+ string::size_type pos;
+ while((pos = result.find(suffix)) != string::npos)
+ {
+ result.replace(pos, suffix.size() - 1, ".rb");
+ }
+ break;
+ }
+ case PHP:
+ {
+ //
+ // Change .o[bj] suffix to .php suffix.
+ //
+ string::size_type pos;
+ while((pos = result.find(suffix)) != string::npos)
+ {
+ result.replace(pos, suffix.size() - 1, ".php");
+ }
+ break;
+ }
+ default:
+ {
+ abort();
+ break;
+ }
+ }
+
+ //
+ // Output result
+ //
+ fputs(result.c_str(), stdout);
+ return true;
+}
+
+bool
+Slice::Preprocessor::close()
+{
+ if(_cppHandle != 0)
+ {
+ int status = fclose(_cppHandle);
+ _cppHandle = 0;
+
+ if(_cppFile.size() != 0)
+ {
+ IceUtilInternal::unlink(_cppFile);
+ }
+
+ if(status != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+Slice::Preprocessor::checkInputFile()
+{
+ string base(_fileName);
+ string suffix;
+ string::size_type pos = base.rfind('.');
+ if(pos != string::npos)
+ {
+ suffix = IceUtilInternal::toLower(base.substr(pos));
+ }
+ if(suffix != ".ice")
+ {
+ getErrorStream() << _path << ": error: input files must end with `.ice'" << endl;
+ return false;
+ }
+
+ ifstream test(_fileName.c_str());
+ if(!test)
+ {
+ getErrorStream() << _path << ": error: cannot open `" << _fileName << "' for reading" << endl;
+ return false;
+ }
+ test.close();
+
+ return true;
+}