1bd9d6d13a8dae0ceb5d79d8223cee8f5b691da5
[app.Builder.js] / src / Palete / VapiParser.vala
1 /* valadoc.vala
2  *
3  * Copyright (C) 2008-2014 Florian Brosch
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18  *
19  * Author:
20  *      Florian Brosch <flo.brosch@gmail.com>
21  */
22
23 // compile? valac --pkg valadoc--pkg libvaladoc-1.0 VapiParser.vala -o /tmp/vdoc
24
25
26 using GLib.Path;
27 using Valadoc.Importer;
28 using Valadoc;
29 //using Config;
30 using Gee;
31
32
33
34 public class ValaDoc : Object {
35         private static string wikidirectory = null;
36         private static string pkg_version = null;
37         private static string docletpath = null;
38         [CCode (array_length = false, array_null_terminated = true)]
39         private static string[] pluginargs;
40         private static string gir_directory = null;
41         private static string directory = null;
42         private static string pkg_name = null;
43         private static string gir_name = null;
44         private static string gir_namespace = null;
45         private static string gir_version = null;
46         private static string driverpath = null;
47
48         private static bool add_inherited = false;
49         private static bool _protected = true;
50         private static bool _internal = false;
51         private static bool with_deps = false;
52         private static bool _private = false;
53         private static bool version = false;
54
55         private static bool verbose = false;
56         private static bool force = false;
57
58         private static string basedir = null;
59         [CCode (array_length = false, array_null_terminated = true)]
60         private static string[] defines;
61         private static bool experimental;
62         private static bool experimental_non_null = false;
63         private static string profile;
64         [CCode (array_length = false, array_null_terminated = true)]
65         private static string[] import_packages;
66         [CCode (array_length = false, array_null_terminated = true)]
67         private static string[] import_directories;
68         [CCode (array_length = false, array_null_terminated = true)]
69         private static string[] vapi_directories;
70         [CCode (array_length = false, array_null_terminated = true)]
71         private static string[] metadata_directories;
72         [CCode (array_length = false, array_null_terminated = true)]
73         private static string[] gir_directories;
74         [CCode (array_length = false, array_null_terminated = true)]
75         private static string[] tsources;
76         [CCode (array_length = false, array_null_terminated = true)]
77         private static string[] packages;
78         static string target_glib;
79
80         private const GLib.OptionEntry[] options = {
81                 { "directory", 'o', 0, OptionArg.FILENAME, ref directory, "Output directory", "DIRECTORY" },
82
83                 { "basedir", 'b', 0, OptionArg.FILENAME, ref basedir, "Base source directory", "DIRECTORY" },
84                 { "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, "Define SYMBOL", "SYMBOL..." },
85                 { "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the default", "PROFILE" },
86
87                 { "enable-experimental", 0, 0, OptionArg.NONE, ref experimental, "Enable experimental features", null },
88                 { "enable-experimental-non-null", 0, 0, OptionArg.NONE, ref experimental_non_null, "Enable experimental enhancements for non-null types", null },
89
90                 { "metadatadir", 0, 0, OptionArg.FILENAME_ARRAY, ref metadata_directories, "Look for GIR .metadata files in DIRECTORY", "DIRECTORY..." },
91                 { "girdir", 0, 0, OptionArg.FILENAME_ARRAY, ref gir_directories, "Look for .gir files in DIRECTORY", "DIRECTORY..." },
92                 { "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
93                 { "pkg", 0, 0, OptionArg.STRING_ARRAY, ref packages, "Include binding for PACKAGE", "PACKAGE..." },
94
95                 { "driver", 0, 0, OptionArg.STRING, ref driverpath, "Name of an driver or path to a custom driver", null },
96
97                 { "importdir", 0, 0, OptionArg.FILENAME_ARRAY, ref import_directories, "Look for external documentation in DIRECTORY", "DIRECTORY..." },
98                 { "import", 0, 0, OptionArg.STRING_ARRAY, ref import_packages, "Include binding for PACKAGE", "PACKAGE..." },
99
100                 { "wiki", 0, 0, OptionArg.FILENAME, ref wikidirectory, "Wiki directory", "DIRECTORY" },
101
102                 { "deps", 0, 0, OptionArg.NONE, ref with_deps, "Adds packages to the documentation", null },
103
104                 { "doclet", 0, 0, OptionArg.STRING, ref docletpath, "Name of an included doclet or path to custom doclet", "PLUGIN"},
105                 { "doclet-arg", 'X', 0, OptionArg.STRING_ARRAY, ref pluginargs, "Pass arguments to the doclet", "ARG" },
106
107                 { "no-protected", 0, OptionFlags.REVERSE, OptionArg.NONE, ref _protected, "Removes protected elements from documentation", null },
108                 { "internal", 0, 0, OptionArg.NONE, ref _internal, "Adds internal elements to documentation", null },
109                 { "private", 0, 0, OptionArg.NONE, ref _private, "Adds private elements to documentation", null },
110
111                 { "package-name", 0, 0, OptionArg.STRING, ref pkg_name, "package name", "NAME" },
112                 { "package-version", 0, 0, OptionArg.STRING, ref pkg_version, "package version", "VERSION" },
113                 { "gir", 0, 0, OptionArg.STRING, ref gir_name, "GObject-Introspection repository file name", "NAME-VERSION.gir" },
114
115                 { "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
116
117                 { "force", 0, 0, OptionArg.NONE, ref force, "force", null },
118                 { "verbose", 0, 0, OptionArg.NONE, ref verbose, "Show all warnings", null },
119                 { "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code generation", "MAJOR.MINOR" },
120                 { "", 0, 0, OptionArg.FILENAME_ARRAY, ref tsources, null, "FILE..." },
121
122                 { null }
123         };
124
125         private static int quit (ErrorReporter reporter) {
126                 if (reporter.errors == 0) {
127                         stdout.printf ("Succeeded - %d warning(s)\n", reporter.warnings);
128                         return 0;
129                 } else {
130                         stdout.printf ("Failed: %d error(s), %d warning(s)\n", reporter.errors, reporter.warnings);
131                         return 1;
132                 }
133         }
134
135         private static bool check_pkg_name () {
136                 if (pkg_name == null) {
137                         return true;
138                 }
139
140                 if (pkg_name == "glib-2.0" || pkg_name == "gobject-2.0") {
141                         return false;
142                 }
143
144                 foreach (string package in tsources) {
145                         if (pkg_name == package) {
146                                 return false;
147                         }
148                 }
149                 return true;
150         }
151
152         private string get_pkg_name () {
153                 if (ValaDoc.pkg_name == null) {
154                         if (ValaDoc.directory.has_suffix ("/")) {
155                                 ValaDoc.pkg_name = GLib.Path.get_dirname (ValaDoc.directory);
156                         } else {
157                                 ValaDoc.pkg_name = GLib.Path.get_basename (ValaDoc.directory);
158                         }
159                 }
160
161                 return ValaDoc.pkg_name;
162         }
163
164         private ModuleLoader? create_module_loader (ErrorReporter reporter, out Doclet? doclet, out Driver? driver) {
165                 ModuleLoader modules = ModuleLoader.get_instance ();
166
167                 doclet = null;
168                 driver = null;
169                 print("docletpath %s", docletpath);
170                 // doclet:
171                 string? pluginpath = ModuleLoader.get_doclet_path (docletpath, reporter);
172                 if (pluginpath == null) {
173                         return null;
174                 }
175
176                 doclet = modules.create_doclet (pluginpath);
177                 if (doclet == null) {
178                         reporter.simple_error ("error: failed to load doclet");
179                         return null;
180                 }
181
182
183                 // driver:
184                 pluginpath = ModuleLoader.get_driver_path (driverpath, reporter);
185                 if (pluginpath == null) {
186                         return null;
187                 }
188
189                 driver = modules.create_driver (pluginpath);
190                 if (driver == null) {
191                         reporter.simple_error ("error: failed to load driver");
192                         return null;
193                 }
194
195                 assert (driver != null && doclet != null);
196
197                 return modules;
198         }
199
200         private int run (ErrorReporter reporter) {
201                 // settings:
202                 var settings = new Valadoc.Settings ();
203                 reporter.settings = settings;
204
205                 settings.pkg_name = this.get_pkg_name ();
206                 settings.gir_namespace = ValaDoc.gir_namespace;
207                 settings.gir_version = ValaDoc.gir_version;
208                 if (ValaDoc.gir_name != null) {
209                         settings.gir_name = GLib.Path.get_basename (ValaDoc.gir_name);
210                         settings.gir_directory = GLib.Path.get_dirname (ValaDoc.gir_name);
211                         if (settings.gir_directory == "") {
212                                 settings.gir_directory = GLib.Path.get_dirname (ValaDoc.directory);
213                         }
214                 }
215                 settings.pkg_version = ValaDoc.pkg_version;
216                 settings.add_inherited = ValaDoc.add_inherited;
217                 settings._protected = ValaDoc._protected;
218                 settings._internal = ValaDoc._internal;
219                 settings.with_deps = ValaDoc.with_deps;
220                 settings._private = ValaDoc._private;
221                 settings.path = realpath (ValaDoc.directory);
222                 settings.verbose = ValaDoc.verbose;
223                 settings.wiki_directory = ValaDoc.wikidirectory;
224                 settings.pluginargs = ValaDoc.pluginargs;
225
226                 settings.experimental = experimental;
227                 settings.experimental_non_null = experimental_non_null;
228                 settings.basedir = basedir;
229                 settings.directory = directory;
230                 settings.vapi_directories = vapi_directories;
231                 settings.metadata_directories = metadata_directories;
232                 settings.gir_directories = gir_directories;
233                 settings.target_glib = target_glib;
234
235                 settings.source_files = tsources;
236                 settings.packages = packages;
237
238                 settings.profile = profile;
239                 settings.defines = defines;
240
241
242                 // load plugins:
243                 Doclet? doclet = null;
244                 Driver? driver = null;
245
246                 ModuleLoader? modules = create_module_loader (reporter, out doclet, out driver);
247                 if (reporter.errors > 0 || modules == null) {
248                         return quit (reporter);
249                 }
250
251
252                 // Create tree:
253                 Valadoc.Api.Tree doctree = driver.build (settings, reporter);
254                 if (reporter.errors > 0) {
255                         driver = null;
256                         doclet = null;
257                         return quit (reporter);
258                 }
259
260                 // register child symbols:
261                 Valadoc.Api.ChildSymbolRegistrar registrar = new Valadoc.Api.ChildSymbolRegistrar ();
262                 doctree.accept (registrar);
263
264                 // process documentation
265                 Valadoc.DocumentationParser docparser = new Valadoc.DocumentationParser (settings, reporter, doctree, modules);
266                 if (!doctree.create_tree()) {
267                         return quit (reporter);
268                 }
269
270                 DocumentationImporter[] importers = {
271                         new ValadocDocumentationImporter (doctree, docparser, modules, settings, reporter),
272                         new GirDocumentationImporter (doctree, docparser, modules, settings, reporter)
273                 };
274
275                 doctree.parse_comments (docparser);
276                 if (reporter.errors > 0) {
277                         return quit (reporter);
278                 }
279
280                 doctree.import_comments (importers, import_packages, import_directories);
281                 if (reporter.errors > 0) {
282                         return quit (reporter);
283                 }
284
285                 doctree.check_comments (docparser);
286                 if (reporter.errors > 0) {
287                         return quit (reporter);
288                 }
289
290                 if (ValaDoc.gir_name != null) {
291                         driver.write_gir (settings, reporter);
292                         if (reporter.errors > 0) {
293                                 return quit (reporter);
294                         }
295                 }
296
297                 doclet.process (settings, doctree, reporter);
298                 return quit (reporter);
299         }
300
301         static int main (string[] args) {
302                 ErrorReporter reporter = new ErrorReporter();
303
304                 try {
305                         var opt_context = new OptionContext ("- Vala Documentation Tool");
306                         opt_context.set_help_enabled (true);
307                         opt_context.add_main_entries (options, null);
308                         opt_context.parse (ref args);
309                 } catch (OptionError e) {
310                         reporter.simple_error ("error: %s", e.message);
311                         stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
312                         return quit (reporter);
313                 }
314
315                 if (version) {
316                         //stdout.printf ("Valadoc %s\n", Config.version);
317                         return 0;
318                 }
319
320                 if (directory == null) {
321                         reporter.simple_error ("error: No output directory specified.");
322                         return quit (reporter);
323                 }
324
325                 if (!check_pkg_name ()) {
326                         reporter.simple_error ("error: File already exists");
327                         return quit (reporter);
328                 }
329
330                 if (FileUtils.test (directory, FileTest.EXISTS)) {
331                         if (force == true) {
332                                 bool tmp = remove_directory (directory);
333                                 if (tmp == false) {
334                                         reporter.simple_error ("error: Can't remove directory.");
335                                         return quit (reporter);
336                                 }
337                         } else {
338                                 reporter.simple_error ("error: File already exists");
339                                 return quit (reporter);
340                         }
341                 }
342
343                 if (wikidirectory != null) {
344                         if (!FileUtils.test(wikidirectory, FileTest.IS_DIR)) {
345                                 reporter.simple_error ("error: Wiki-directory does not exist.");
346                                 return quit (reporter);
347                         }
348                 }
349
350                 if (gir_name != null) {
351                         long gir_len = gir_name.length;
352                         int last_hyphen = gir_name.last_index_of_char ('-');
353
354                         if (last_hyphen == -1 || !gir_name.has_suffix (".gir")) {
355                                 reporter.simple_error ("error: GIR file name `%s' is not well-formed, expected NAME-VERSION.gir", gir_name);
356                                 return quit (reporter);
357                         }
358
359                         gir_namespace = gir_name.substring (0, last_hyphen);
360                         gir_version = gir_name.substring (last_hyphen + 1, gir_len - last_hyphen - 5);
361                         gir_version.canon ("0123456789.", '?');
362
363                         if (gir_namespace == "" || gir_version == "" || !gir_version[0].isdigit () || gir_version.contains ("?")) {
364                                 reporter.simple_error ("error: GIR file name `%s' is not well-formed, expected NAME-VERSION.gir", gir_name);
365                                 return quit (reporter);
366                         }
367
368
369                         bool report_warning = true;
370                         foreach (string source in tsources) {
371                                 if (source.has_suffix (".vala") || source.has_suffix (".gs")) {
372                                         report_warning = false;
373                                         break;
374                                 }
375                         }
376
377                         if (report_warning == true) {
378                                 reporter.simple_error ("error: No source file specified to be compiled to gir.");
379                                 return quit (reporter);
380                         }
381                 }
382
383                 var valadoc = new ValaDoc( );
384                 return valadoc.run (reporter);
385         }
386 }
387