Skip to content

Commit 6825eeb

Browse files
committed
This is the next step in building the standalone tools infrastructure:
This patch simplifies writing of standalone Clang tools. As an example, we add clang-check, a tool that runs a syntax only frontend action over a .cc file. When you integrate this into your favorite editor, you get much faster feedback on your compilation errors, thus reducing your feedback cycle especially when writing new code. The tool depends on integration of an outstanding patch to CMake to work which allows you to always have a current compile command database in your cmake output directory when you set CMAKE_EXPORT_COMPILE_COMMANDS. llvm-svn: 130306
1 parent 27c0c9b commit 6825eeb

File tree

11 files changed

+909
-5
lines changed

11 files changed

+909
-5
lines changed

clang/examples/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
add_subdirectory(clang-interpreter)
22
add_subdirectory(PrintFunctionNames)
3-
3+
add_subdirectory(Tooling)

clang/examples/Tooling/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
set(LLVM_USED_LIBS clangTooling clangBasic)
2+
3+
add_clang_executable(clang-check
4+
ClangCheck.cpp
5+
)
6+

clang/examples/Tooling/ClangCheck.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//===- examples/Tooling/ClangCheck.cpp - Clang check tool -----------------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file implements a clang-check tool that runs the
11+
// clang::SyntaxOnlyAction over a number of translation units.
12+
//
13+
// Usage:
14+
// clang-check <cmake-output-dir> <file1> <file2> ...
15+
//
16+
// Where <cmake-output-dir> is a CMake build directory in which a file named
17+
// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
18+
// CMake to get this output).
19+
//
20+
// <file1> ... specify the paths of files in the CMake source tree. This path
21+
// is looked up in the compile command database. If the path of a file is
22+
// absolute, it needs to point into CMake's source tree. If the path is
23+
// relative, the current working directory needs to be in the CMake source
24+
// tree and the file must be in a subdirectory of the current working
25+
// directory. "./" prefixes in the relative files will be automatically
26+
// removed, but the rest of a relative path must be a suffix of a path in
27+
// the compile command line database.
28+
//
29+
// For example, to use clang-check on all files in a subtree of the source
30+
// tree, use:
31+
// /path/to/cmake/sources $ find . -name '*.cpp' \
32+
// |xargs clang-check /path/to/cmake/build
33+
//
34+
//===----------------------------------------------------------------------===//
35+
36+
#include "clang/Frontend/FrontendActions.h"
37+
#include "clang/Tooling/Tooling.h"
38+
#include "llvm/ADT/OwningPtr.h"
39+
#include "llvm/ADT/Twine.h"
40+
#include "llvm/Support/MemoryBuffer.h"
41+
#include "llvm/Support/Path.h"
42+
#include "llvm/Support/raw_ostream.h"
43+
#include "llvm/Support/system_error.h"
44+
45+
/// \brief Returns the absolute path of 'File', by prepending it with
46+
/// 'BaseDirectory' if 'File' is not absolute. Otherwise returns 'File'.
47+
/// If 'File' starts with "./", the returned path will not contain the "./".
48+
/// Otherwise, the returned path will contain the literal path-concatenation of
49+
/// 'BaseDirectory' and 'File'.
50+
///
51+
/// \param File Either an absolute or relative path.
52+
/// \param BaseDirectory An absolute path.
53+
///
54+
/// FIXME: Put this somewhere where it is more generally available.
55+
static std::string GetAbsolutePath(
56+
llvm::StringRef File, llvm::StringRef BaseDirectory) {
57+
assert(llvm::sys::path::is_absolute(BaseDirectory));
58+
if (llvm::sys::path::is_absolute(File)) {
59+
return File;
60+
}
61+
llvm::StringRef RelativePath(File);
62+
if (RelativePath.startswith("./")) {
63+
RelativePath = RelativePath.substr(strlen("./"));
64+
}
65+
llvm::SmallString<1024> AbsolutePath(BaseDirectory);
66+
llvm::sys::path::append(AbsolutePath, RelativePath);
67+
return AbsolutePath.str();
68+
}
69+
70+
int main(int argc, char **argv) {
71+
if (argc < 3) {
72+
llvm::outs() << "Usage: " << argv[0] << " <cmake-output-dir> "
73+
<< "<file1> <file2> ...\n";
74+
return 1;
75+
}
76+
// FIXME: We should pull how to find the database into the Tooling package.
77+
llvm::OwningPtr<llvm::MemoryBuffer> JsonDatabase;
78+
llvm::SmallString<1024> JsonDatabasePath(argv[1]);
79+
llvm::sys::path::append(JsonDatabasePath, "compile_commands.json");
80+
llvm::error_code Result =
81+
llvm::MemoryBuffer::getFile(JsonDatabasePath, JsonDatabase);
82+
if (Result != 0) {
83+
llvm::outs() << "Error while opening JSON database: " << Result.message()
84+
<< "\n";
85+
return 1;
86+
}
87+
llvm::StringRef BaseDirectory(::getenv("PWD"));
88+
for (int I = 2; I < argc; ++I) {
89+
llvm::SmallString<1024> File(GetAbsolutePath(argv[I], BaseDirectory));
90+
llvm::outs() << "Processing " << File << ".\n";
91+
std::string ErrorMessage;
92+
clang::tooling::CompileCommand LookupResult =
93+
clang::tooling::FindCompileArgsInJsonDatabase(
94+
File.str(), JsonDatabase->getBuffer(), ErrorMessage);
95+
if (!LookupResult.CommandLine.empty()) {
96+
if (!clang::tooling::RunToolWithFlags(
97+
new clang::SyntaxOnlyAction,
98+
LookupResult.CommandLine.size(),
99+
clang::tooling::CommandLineToArgv(
100+
&LookupResult.CommandLine).data())) {
101+
llvm::outs() << "Error while processing " << File << ".\n";
102+
}
103+
} else {
104+
llvm::outs() << "Skipping " << File << ". Command line not found.\n";
105+
}
106+
}
107+
return 0;
108+
}

clang/include/clang/Tooling/Tooling.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#define LLVM_CLANG_TOOLING_TOOLING_H
1717

1818
#include "llvm/ADT/StringRef.h"
19+
#include <string>
20+
#include <vector>
1921

2022
namespace clang {
2123

@@ -26,12 +28,53 @@ namespace tooling {
2628
/// \brief Runs (and deletes) the tool on 'Code' with the -fsynatx-only flag.
2729
///
2830
/// \param ToolAction The action to run over the code.
29-
// \param Code C++ code.
31+
/// \param Code C++ code.
3032
///
3133
/// \return - True if 'ToolAction' was successfully executed.
3234
bool RunSyntaxOnlyToolOnCode(
3335
clang::FrontendAction *ToolAction, llvm::StringRef Code);
3436

37+
/// \brief Runs (and deletes) the tool with the given Clang flags.
38+
///
39+
/// \param ToolAction The action to run over the code.
40+
/// \param Argc The number of elements in Argv.
41+
/// \param Argv The command line arguments, including the path the binary
42+
/// was started with (Argv[0]).
43+
bool RunToolWithFlags(
44+
clang::FrontendAction* ToolAction, int Argc, char *Argv[]);
45+
46+
/// \brief Converts a vector<string> into a vector<char*> suitable to pass
47+
/// to main-style functions taking (int Argc, char *Argv[]).
48+
std::vector<char*> CommandLineToArgv(const std::vector<std::string>* Command);
49+
50+
/// \brief Specifies the working directory and command of a compilation.
51+
struct CompileCommand {
52+
/// \brief The working directory the command was executed from.
53+
std::string Directory;
54+
55+
/// \brief The command line that was executed.
56+
std::vector<std::string> CommandLine;
57+
};
58+
59+
/// \brief Looks up the compile command for 'FileName' in 'JsonDatabase'.
60+
///
61+
/// \param FileName The path to an input file for which we want the compile
62+
/// command line. If the 'JsonDatabase' was created by CMake, this must be
63+
/// an absolute path inside the CMake source directory which does not have
64+
/// symlinks resolved.
65+
///
66+
/// \param JsonDatabase A JSON formatted list of compile commands. This lookup
67+
/// command supports only a subset of the JSON standard as written by CMake.
68+
///
69+
/// \param ErrorMessage If non-empty, an error occurred and 'ErrorMessage' will
70+
/// be set to contain the error message. In this case CompileCommand will
71+
/// contain an empty directory and command line.
72+
///
73+
/// \see JsonCompileCommandLineDatabase
74+
CompileCommand FindCompileArgsInJsonDatabase(
75+
llvm::StringRef FileName, llvm::StringRef JsonDatabase,
76+
std::string &ErrorMessage);
77+
3578
} // end namespace tooling
3679
} // end namespace clang
3780

clang/lib/Tooling/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
SET(LLVM_USED_LIBS clangBasic clangFrontend clangAST)
22

33
add_clang_library(clangTooling
4+
JsonCompileCommandLineDatabase.cpp
45
Tooling.cpp
56
)

0 commit comments

Comments
 (0)