1/*
2 * Copyright 2011 Sven Verdoolaege. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 *    1. Redistributions of source code must retain the above copyright
9 *       notice, this list of conditions and the following disclaimer.
10 *
11 *    2. Redistributions in binary form must reproduce the above
12 *       copyright notice, this list of conditions and the following
13 *       disclaimer in the documentation and/or other materials provided
14 *       with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY SVEN VERDOOLAEGE ''AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation
29 * are those of the authors and should not be interpreted as
30 * representing official policies, either expressed or implied, of
31 * Sven Verdoolaege.
32 */
33
34#include "isl_config.h"
35
36#include <assert.h>
37#include <iostream>
38#include <llvm/Support/raw_ostream.h>
39#include <llvm/Support/CommandLine.h>
40#include <llvm/Support/Host.h>
41#include <llvm/Support/ManagedStatic.h>
42#include <clang/AST/ASTContext.h>
43#include <clang/AST/ASTConsumer.h>
44#include <clang/Basic/FileSystemOptions.h>
45#include <clang/Basic/FileManager.h>
46#include <clang/Basic/TargetOptions.h>
47#include <clang/Basic/TargetInfo.h>
48#include <clang/Basic/Version.h>
49#include <clang/Driver/Compilation.h>
50#include <clang/Driver/Driver.h>
51#include <clang/Driver/Tool.h>
52#include <clang/Frontend/CompilerInstance.h>
53#include <clang/Frontend/CompilerInvocation.h>
54#ifdef HAVE_BASIC_DIAGNOSTICOPTIONS_H
55#include <clang/Basic/DiagnosticOptions.h>
56#else
57#include <clang/Frontend/DiagnosticOptions.h>
58#endif
59#include <clang/Frontend/TextDiagnosticPrinter.h>
60#include <clang/Frontend/Utils.h>
61#include <clang/Lex/HeaderSearch.h>
62#include <clang/Lex/Preprocessor.h>
63#include <clang/Parse/ParseAST.h>
64#include <clang/Sema/Sema.h>
65
66#include "extract_interface.h"
67#include "python.h"
68
69using namespace std;
70using namespace clang;
71using namespace clang::driver;
72
73static llvm::cl::opt<string> InputFilename(llvm::cl::Positional,
74			llvm::cl::Required, llvm::cl::desc("<input file>"));
75static llvm::cl::list<string> Includes("I",
76			llvm::cl::desc("Header search path"),
77			llvm::cl::value_desc("path"), llvm::cl::Prefix);
78
79static const char *ResourceDir = CLANG_PREFIX"/lib/clang/"CLANG_VERSION_STRING;
80
81/* Does decl have an attribute of the following form?
82 *
83 *	__attribute__((annotate("name")))
84 */
85bool has_annotation(Decl *decl, const char *name)
86{
87	if (!decl->hasAttrs())
88		return false;
89
90	AttrVec attrs = decl->getAttrs();
91	for (AttrVec::const_iterator i = attrs.begin() ; i != attrs.end(); ++i) {
92		const AnnotateAttr *ann = dyn_cast<AnnotateAttr>(*i);
93		if (!ann)
94			continue;
95		if (ann->getAnnotation().str() == name)
96			return true;
97	}
98
99	return false;
100}
101
102/* Is decl marked as exported?
103 */
104static bool is_exported(Decl *decl)
105{
106	return has_annotation(decl, "isl_export");
107}
108
109/* Collect all types and functions that are annotated "isl_export"
110 * in "types" and "function".
111 *
112 * We currently only consider single declarations.
113 */
114struct MyASTConsumer : public ASTConsumer {
115	set<RecordDecl *> types;
116	set<FunctionDecl *> functions;
117
118	virtual HandleTopLevelDeclReturn HandleTopLevelDecl(DeclGroupRef D) {
119		Decl *decl;
120
121		if (!D.isSingleDecl())
122			return HandleTopLevelDeclContinue;
123		decl = D.getSingleDecl();
124		if (!is_exported(decl))
125			return HandleTopLevelDeclContinue;
126		switch (decl->getKind()) {
127		case Decl::Record:
128			types.insert(cast<RecordDecl>(decl));
129			break;
130		case Decl::Function:
131			functions.insert(cast<FunctionDecl>(decl));
132			break;
133		default:
134			break;
135		}
136		return HandleTopLevelDeclContinue;
137	}
138};
139
140#ifdef USE_ARRAYREF
141
142#ifdef HAVE_CXXISPRODUCTION
143static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
144{
145	return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
146			    "", false, false, Diags);
147}
148#elif defined(HAVE_ISPRODUCTION)
149static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
150{
151	return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
152			    "", false, Diags);
153}
154#else
155static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
156{
157	return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
158			    "", Diags);
159}
160#endif
161
162/* Create a CompilerInvocation object that stores the command line
163 * arguments constructed by the driver.
164 * The arguments are mainly useful for setting up the system include
165 * paths on newer clangs and on some platforms.
166 */
167static CompilerInvocation *construct_invocation(const char *filename,
168	DiagnosticsEngine &Diags)
169{
170	const char *binary = CLANG_PREFIX"/bin/clang";
171	const llvm::OwningPtr<Driver> driver(construct_driver(binary, Diags));
172	std::vector<const char *> Argv;
173	Argv.push_back(binary);
174	Argv.push_back(filename);
175	const llvm::OwningPtr<Compilation> compilation(
176		driver->BuildCompilation(llvm::ArrayRef<const char *>(Argv)));
177	JobList &Jobs = compilation->getJobs();
178
179	Command *cmd = cast<Command>(*Jobs.begin());
180	if (strcmp(cmd->getCreator().getName(), "clang"))
181		return NULL;
182
183	const ArgStringList *args = &cmd->getArguments();
184
185	CompilerInvocation *invocation = new CompilerInvocation;
186	CompilerInvocation::CreateFromArgs(*invocation, args->data() + 1,
187						args->data() + args->size(),
188						Diags);
189	return invocation;
190}
191
192#else
193
194static CompilerInvocation *construct_invocation(const char *filename,
195	DiagnosticsEngine &Diags)
196{
197	return NULL;
198}
199
200#endif
201
202#ifdef HAVE_BASIC_DIAGNOSTICOPTIONS_H
203
204static TextDiagnosticPrinter *construct_printer(void)
205{
206	return new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions());
207}
208
209#else
210
211static TextDiagnosticPrinter *construct_printer(void)
212{
213	DiagnosticOptions DO;
214	return new TextDiagnosticPrinter(llvm::errs(), DO);
215}
216
217#endif
218
219#ifdef CREATETARGETINFO_TAKES_POINTER
220
221static TargetInfo *create_target_info(CompilerInstance *Clang,
222	DiagnosticsEngine &Diags)
223{
224	TargetOptions &TO = Clang->getTargetOpts();
225	TO.Triple = llvm::sys::getDefaultTargetTriple();
226	return TargetInfo::CreateTargetInfo(Diags, &TO);
227}
228
229#else
230
231static TargetInfo *create_target_info(CompilerInstance *Clang,
232	DiagnosticsEngine &Diags)
233{
234	TargetOptions &TO = Clang->getTargetOpts();
235	TO.Triple = llvm::sys::getDefaultTargetTriple();
236	return TargetInfo::CreateTargetInfo(Diags, TO);
237}
238
239#endif
240
241#ifdef CREATEDIAGNOSTICS_TAKES_ARG
242
243static void create_diagnostics(CompilerInstance *Clang)
244{
245	Clang->createDiagnostics(0, NULL, construct_printer());
246}
247
248#else
249
250static void create_diagnostics(CompilerInstance *Clang)
251{
252	Clang->createDiagnostics(construct_printer());
253}
254
255#endif
256
257#ifdef ADDPATH_TAKES_4_ARGUMENTS
258
259void add_path(HeaderSearchOptions &HSO, string Path)
260{
261	HSO.AddPath(Path, frontend::Angled, false, false);
262}
263
264#else
265
266void add_path(HeaderSearchOptions &HSO, string Path)
267{
268	HSO.AddPath(Path, frontend::Angled, true, false, false);
269}
270
271#endif
272
273int main(int argc, char *argv[])
274{
275	llvm::cl::ParseCommandLineOptions(argc, argv);
276
277	CompilerInstance *Clang = new CompilerInstance();
278	create_diagnostics(Clang);
279	DiagnosticsEngine &Diags = Clang->getDiagnostics();
280	Diags.setSuppressSystemWarnings(true);
281	CompilerInvocation *invocation =
282		construct_invocation(InputFilename.c_str(), Diags);
283	if (invocation)
284		Clang->setInvocation(invocation);
285	Clang->createFileManager();
286	Clang->createSourceManager(Clang->getFileManager());
287	TargetInfo *target = create_target_info(Clang, Diags);
288	Clang->setTarget(target);
289	CompilerInvocation::setLangDefaults(Clang->getLangOpts(), IK_C,
290					    LangStandard::lang_unspecified);
291	HeaderSearchOptions &HSO = Clang->getHeaderSearchOpts();
292	LangOptions &LO = Clang->getLangOpts();
293	PreprocessorOptions &PO = Clang->getPreprocessorOpts();
294	HSO.ResourceDir = ResourceDir;
295
296	for (int i = 0; i < Includes.size(); ++i)
297		add_path(HSO, Includes[i]);
298
299	PO.addMacroDef("__isl_give=__attribute__((annotate(\"isl_give\")))");
300	PO.addMacroDef("__isl_keep=__attribute__((annotate(\"isl_keep\")))");
301	PO.addMacroDef("__isl_take=__attribute__((annotate(\"isl_take\")))");
302	PO.addMacroDef("__isl_export=__attribute__((annotate(\"isl_export\")))");
303	PO.addMacroDef("__isl_constructor=__attribute__((annotate(\"isl_constructor\"))) __attribute__((annotate(\"isl_export\")))");
304	PO.addMacroDef("__isl_subclass(super)=__attribute__((annotate(\"isl_subclass(\" #super \")\"))) __attribute__((annotate(\"isl_export\")))");
305
306	Clang->createPreprocessor();
307	Preprocessor &PP = Clang->getPreprocessor();
308
309	PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), LO);
310
311	const FileEntry *file = Clang->getFileManager().getFile(InputFilename);
312	assert(file);
313	Clang->getSourceManager().createMainFileID(file);
314
315	Clang->createASTContext();
316	MyASTConsumer consumer;
317	Sema *sema = new Sema(PP, Clang->getASTContext(), consumer);
318
319	Diags.getClient()->BeginSourceFile(LO, &PP);
320	ParseAST(*sema);
321	Diags.getClient()->EndSourceFile();
322
323	generate_python(consumer.types, consumer.functions);
324
325	delete sema;
326	delete Clang;
327	llvm::llvm_shutdown();
328
329	return 0;
330}
331