src/Palete/VapiParser.vala
[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-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                  
209                 settings.pkg_version = ValaDoc.pkg_version;
210                 settings.add_inherited = ValaDoc.add_inherited;
211                 settings._protected = ValaDoc._protected;
212                 settings._internal = ValaDoc._internal;
213                 settings.with_deps = ValaDoc.with_deps;
214                 settings._private = ValaDoc._private;
215                 settings.path = "/dev/null";
216                 settings.verbose = ValaDoc.verbose;
217                 settings.wiki_directory = ValaDoc.wikidirectory;
218                 settings.pluginargs = ValaDoc.pluginargs;
219
220                 settings.experimental = experimental;
221                 settings.experimental_non_null = experimental_non_null;
222                 settings.basedir = basedir;
223                 settings.directory = directory;
224                 settings.vapi_directories = vapi_directories;
225                 settings.metadata_directories = metadata_directories;
226                 settings.gir_directories = gir_directories;
227                 settings.target_glib = target_glib;
228
229                 settings.source_files = tsources;
230                 settings.packages = packages;
231
232                 settings.profile = profile;
233                 settings.defines = defines;
234
235
236                 // load plugins:
237                 Doclet? doclet = null;
238                 Driver? driver = null;
239
240                 ModuleLoader? modules = create_module_loader (reporter, out doclet, out driver);
241                 if (reporter.errors > 0 || modules == null) {
242                         return quit (reporter);
243                 }
244
245
246                 // Create tree:
247                 Valadoc.Api.Tree doctree = driver.build (settings, reporter);
248                 if (reporter.errors > 0) {
249                         driver = null;
250                         doclet = null;
251                         return quit (reporter);
252                 }
253
254                 // register child symbols:
255                 Valadoc.Api.ChildSymbolRegistrar registrar = new Valadoc.Api.ChildSymbolRegistrar ();
256                 doctree.accept (registrar);
257
258                 // process documentation
259                 Valadoc.DocumentationParser docparser = new Valadoc.DocumentationParser (settings, reporter, doctree, modules);
260                 if (!doctree.create_tree()) {
261                         return quit (reporter);
262                 }
263
264                 DocumentationImporter[] importers = {
265                         new ValadocDocumentationImporter (doctree, docparser, modules, settings, reporter),
266                         new GirDocumentationImporter (doctree, docparser, modules, settings, reporter)
267                 };
268
269                 doctree.parse_comments (docparser);
270                 if (reporter.errors > 0) {
271                         return quit (reporter);
272                 }
273
274                 doctree.import_comments (importers, import_packages, import_directories);
275                 if (reporter.errors > 0) {
276                         return quit (reporter);
277                 }
278
279                 doctree.check_comments (docparser);
280                 if (reporter.errors > 0) {
281                         return quit (reporter);
282                 }
283
284                 if (ValaDoc.gir_name != null) {
285                         driver.write_gir (settings, reporter);
286                         if (reporter.errors > 0) {
287                                 return quit (reporter);
288                         }
289                 }
290
291                 doclet.process (settings, doctree, reporter);
292                 return quit (reporter);
293         }
294
295         static int main (string[] args) {
296                 ErrorReporter reporter = new ErrorReporter();
297
298                 try {
299                         var opt_context = new OptionContext ("- Vala Documentation Tool");
300                         opt_context.set_help_enabled (true);
301                         opt_context.add_main_entries (options, null);
302                         opt_context.parse (ref args);
303                 } catch (OptionError e) {
304                         reporter.simple_error ("error: %s", e.message);
305                         stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
306                         return quit (reporter);
307                 }
308
309                 if (version) {
310                         //stdout.printf ("Valadoc %s\n", Config.version);
311                         return 0;
312                 }
313  
314                 if (!check_pkg_name ()) {
315                         reporter.simple_error ("error: File already exists");
316                         return quit (reporter);
317                 }
318 /*
319                 if (FileUtils.test (directory, FileTest.EXISTS)) {
320                         if (force == true) {
321                                 bool tmp = remove_directory (directory);
322                                 if (tmp == false) {
323                                         reporter.simple_error ("error: Can't remove directory.");
324                                         return quit (reporter);
325                                 }
326                         } else {
327                                 reporter.simple_error ("error: File already exists");
328                                 return quit (reporter);
329                         }
330                 }
331 */
332         
333                 if (gir_name != null) {
334                         long gir_len = gir_name.length;
335                         int last_hyphen = gir_name.last_index_of_char ('-');
336
337                         if (last_hyphen == -1 || !gir_name.has_suffix (".gir")) {
338                                 reporter.simple_error ("error: GIR file name `%s' is not well-formed, expected NAME-VERSION.gir", gir_name);
339                                 return quit (reporter);
340                         }
341
342                         gir_namespace = gir_name.substring (0, last_hyphen);
343                         gir_version = gir_name.substring (last_hyphen + 1, gir_len - last_hyphen - 5);
344                         gir_version.canon ("0123456789.", '?');
345
346                         if (gir_namespace == "" || gir_version == "" || !gir_version[0].isdigit () || gir_version.contains ("?")) {
347                                 reporter.simple_error ("error: GIR file name `%s' is not well-formed, expected NAME-VERSION.gir", gir_name);
348                                 return quit (reporter);
349                         }
350
351
352                         bool report_warning = true;
353                         foreach (string source in tsources) {
354                                 if (source.has_suffix (".vala") || source.has_suffix (".gs")) {
355                                         report_warning = false;
356                                         break;
357                                 }
358                         }
359
360                         if (report_warning == true) {
361                                 reporter.simple_error ("error: No source file specified to be compiled to gir.");
362                                 return quit (reporter);
363                         }
364                 }
365
366                 var valadoc = new ValaDoc( );
367                 return valadoc.run (reporter);
368         }
369 }
370