summaryrefslogtreecommitdiffstats
path: root/Source/cmVariableWatch.cxx
blob: d234f28d2a36e8938d63778da574bfc6fbdcb362 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*=========================================================================

  Program:   CMake - Cross-Platform Makefile Generator
  Module:    $RCSfile$
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "cmVariableWatch.h"

static const char* const cmVariableWatchAccessStrings[] =
{
    "READ_ACCESS",
    "UNKNOWN_READ_ACCESS",
    "ALLOWED_UNKNOWN_READ_ACCESS",
    "MODIFIED_ACCESS",
    "REMOVED_ACCESS",
    "NO_ACCESS"
};

const char* cmVariableWatch::GetAccessAsString(int access_type)
{
  if ( access_type < 0 || access_type >= cmVariableWatch::NO_ACCESS )
    {
    return "NO_ACCESS";
    }
  return cmVariableWatchAccessStrings[access_type];
}

cmVariableWatch::cmVariableWatch()
{
}

cmVariableWatch::~cmVariableWatch()
{
}

void cmVariableWatch::AddWatch(const std::string& variable, 
                               WatchMethod method, void* client_data /*=0*/)
{
  cmVariableWatch::Pair p;
  p.Method = method;
  p.ClientData = client_data;
  cmVariableWatch::VectorOfPairs* vp = &this->WatchMap[variable];
  cmVariableWatch::VectorOfPairs::size_type cc;
  for ( cc = 0; cc < vp->size(); cc ++ )
    {
    cmVariableWatch::Pair* pair = &(*vp)[cc];
    if ( pair->Method == method )
      {
      (*vp)[cc] = p;
      return;
      }
    }
  vp->push_back(p);
}

void cmVariableWatch::RemoveWatch(const std::string& variable, 
                                  WatchMethod method)
{
  cmVariableWatch::VectorOfPairs* vp = &this->WatchMap[variable];
  cmVariableWatch::VectorOfPairs::iterator it;
  for ( it = vp->begin(); it != vp->end(); ++it )
    {
    if ( it->Method == method )
      {
      vp->erase(it);
      return;
      }
    }
}

void  cmVariableWatch::VariableAccessed(const std::string& variable, 
                                        int access_type,
                                        const char* newValue,
                                        const cmMakefile* mf) const
{
  cmVariableWatch::StringToVectorOfPairs::const_iterator mit = 
    this->WatchMap.find(variable);
  if ( mit  != this->WatchMap.end() )
    {
    const cmVariableWatch::VectorOfPairs* vp = &mit->second;
    cmVariableWatch::VectorOfPairs::const_iterator it;
    for ( it = vp->begin(); it != vp->end(); it ++ )
      {
      it->Method(variable, access_type, it->ClientData,
        newValue, mf);
      }
    }
}
an class="hl slc">// exit() may block on locks held by those threads, so forcibly exit. fflush(stderr); fflush(stdout); ExitProcess(1); } static void usage(const char* msg) { Fatal("%s\n\nusage:\n " "cmcldeps " "<language C, CXX or RC> " "<source file path> " "<output path for *.d file> " "<output path for *.obj file> " "<prefix of /showIncludes> " "<path to cl.exe> " "<path to tool (cl or rc)> " "<rest of command ...>\n", msg); } static std::string trimLeadingSpace(const std::string& cmdline) { int i = 0; for (; cmdline[i] == ' '; ++i) ; return cmdline.substr(i); } static void replaceAll(std::string& str, const std::string& search, const std::string& repl) { std::string::size_type pos = 0; while ((pos = str.find(search, pos)) != std::string::npos) { str.replace(pos, search.size(), repl); pos += repl.size(); } } bool startsWith(const std::string& str, const std::string& what) { return str.compare(0, what.size(), what) == 0; } // Strips one argument from the cmdline and returns it. "surrounding quotes" // are removed from the argument if there were any. static std::string getArg(std::string& cmdline) { std::string ret; bool in_quoted = false; unsigned int i = 0; cmdline = trimLeadingSpace(cmdline); for (;; ++i) { if (i >= cmdline.size()) usage("Couldn't parse arguments."); if (!in_quoted && cmdline[i] == ' ') break; // "a b" "x y" if (cmdline[i] == '"') in_quoted = !in_quoted; } ret = cmdline.substr(0, i); if (ret[0] == '"' && ret[i - 1] == '"') ret = ret.substr(1, ret.size() - 2); cmdline = cmdline.substr(i); return ret; } static void parseCommandLine(LPWSTR wincmdline, std::string& lang, std::string& srcfile, std::string& dfile, std::string& objfile, std::string& prefix, std::string& clpath, std::string& binpath, std::string& rest) { std::string cmdline = cmsys::Encoding::ToNarrow(wincmdline); /* self */ getArg(cmdline); lang = getArg(cmdline); srcfile = getArg(cmdline); dfile = getArg(cmdline); objfile = getArg(cmdline); prefix = getArg(cmdline); clpath = getArg(cmdline); binpath = getArg(cmdline); rest = trimLeadingSpace(cmdline); } // Not all backslashes need to be escaped in a depfile, but it's easier that // way. See the re2c grammar in ninja's source code for more info. static void escapePath(std::string& path) { replaceAll(path, "\\", "\\\\"); replaceAll(path, " ", "\\ "); } static void outputDepFile(const std::string& dfile, const std::string& objfile, std::vector<std::string>& incs) { if (dfile.empty()) return; // strip duplicates std::sort(incs.begin(), incs.end()); incs.erase(std::unique(incs.begin(), incs.end()), incs.end()); FILE* out = cmsys::SystemTools::Fopen(dfile.c_str(), "wb"); // FIXME should this be fatal or not? delete obj? delete d? if (!out) return; std::string cwd = cmSystemTools::GetCurrentWorkingDirectory(); replaceAll(cwd, "/", "\\"); cwd += "\\"; std::string tmp = objfile; escapePath(tmp); fprintf(out, "%s: \\\n", tmp.c_str()); std::vector<std::string>::iterator it = incs.begin(); for (; it != incs.end(); ++it) { tmp = *it; // The paths need to match the ones used to identify build artifacts in the // build.ninja file. Therefore we need to canonicalize the path to use // backward slashes and relativize the path to the build directory. replaceAll(tmp, "/", "\\"); if (startsWith(tmp, cwd)) tmp = tmp.substr(cwd.size()); escapePath(tmp); fprintf(out, "%s \\\n", tmp.c_str()); } fprintf(out, "\n"); fclose(out); } bool contains(const std::string& str, const std::string& what) { return str.find(what) != std::string::npos; } std::string replace(const std::string& str, const std::string& what, const std::string& replacement) { size_t pos = str.find(what); if (pos == std::string::npos) return str; std::string replaced = str; return replaced.replace(pos, what.size(), replacement); } static int process(const std::string& srcfilename, const std::string& dfile, const std::string& objfile, const std::string& prefix, const std::string& cmd, const std::string& dir = "", bool quiet = false) { std::string output; // break up command line into a vector std::vector<std::string> args; cmSystemTools::ParseWindowsCommandLine(cmd.c_str(), args); // convert to correct vector type for RunSingleCommand std::vector<std::string> command; for (std::vector<std::string>::iterator i = args.begin(); i != args.end(); ++i) { command.push_back(*i); } // run the command int exit_code = 0; bool run = cmSystemTools::RunSingleCommand(command, &output, &output, &exit_code, dir.c_str(), cmSystemTools::OUTPUT_NONE); // process the include directives and output everything else std::istringstream ss(output); std::string line; std::vector<std::string> includes; bool isFirstLine = true; // cl prints always first the source filename while (std::getline(ss, line)) { if (startsWith(line, prefix)) { std::string inc = trimLeadingSpace(line.substr(prefix.size()).c_str()); if (inc[inc.size() - 1] == '\r') // blech, stupid \r\n inc = inc.substr(0, inc.size() - 1); includes.push_back(inc); } else { if (!isFirstLine || !startsWith(line, srcfilename)) { if (!quiet || exit_code != 0) { fprintf(stdout, "%s\n", line.c_str()); } } else { isFirstLine = false; } } } // don't update .d until/unless we succeed compilation if (run && exit_code == 0) outputDepFile(dfile, objfile, includes); return exit_code; } int main() { // Use the Win32 API instead of argc/argv so we can avoid interpreting the // rest of command line after the .d and .obj. Custom parsing seemed // preferable to the ugliness you get into in trying to re-escape quotes for // subprocesses, so by avoiding argc/argv, the subprocess is called with // the same command line verbatim. std::string lang, srcfile, dfile, objfile, prefix, cl, binpath, rest; parseCommandLine(GetCommandLineW(), lang, srcfile, dfile, objfile, prefix, cl, binpath, rest); // needed to suppress filename output of msvc tools std::string srcfilename; { std::string::size_type pos = srcfile.rfind('\\'); if (pos == std::string::npos) { srcfilename = srcfile; } else { srcfilename = srcfile.substr(pos + 1); } } std::string nol = " /nologo "; std::string show = " /showIncludes "; if (lang == "C" || lang == "CXX") { return process(srcfilename, dfile, objfile, prefix, binpath + nol + show + rest); } else if (lang == "RC") { // "misuse" cl.exe to get headers from .rc files std::string clrest = rest; // rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj clrest = replace(clrest, "/fo", "/out:"); clrest = replace(clrest, objfile, objfile + ".dep.obj "); cl = "\"" + cl + "\" /P /DRC_INVOKED /TC "; // call cl in object dir so the .i is generated there std::string objdir; { std::string::size_type pos = objfile.rfind("\\"); if (pos != std::string::npos) { objdir = objfile.substr(0, pos); } } // extract dependencies with cl.exe int exit_code = process(srcfilename, dfile, objfile, prefix, cl + nol + show + clrest, objdir, true); if (exit_code != 0) return exit_code; // compile rc file with rc.exe return process(srcfilename, "", objfile, prefix, binpath + " " + rest); } usage("Invalid language specified."); return 1; }