tests: Add basic GLib.Node test
[vala-gnome.git] / valadoc / valadoc.vala
blob11263a0f76d627d88f13953588910d1c1267dbc2
1 /* valadoc.vala
3 * Copyright (C) 2008-2014 Florian Brosch
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.
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.
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
19 * Author:
20 * Florian Brosch <flo.brosch@gmail.com>
23 using Valadoc.Importer;
24 using Valadoc;
26 public class ValaDoc : Object {
27 private const string DEFAULT_COLORS = "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01";
29 private static string wikidirectory = null;
30 private static string pkg_version = null;
31 private static string docletpath = null;
32 [CCode (array_length = false, array_null_terminated = true)]
33 private static string[] pluginargs;
34 private static string directory = null;
35 private static string pkg_name = null;
36 private static string gir_name = null;
37 private static string gir_namespace = null;
38 private static string gir_version = null;
39 private static string driverpath = null;
41 private static bool add_inherited = false;
42 private static bool _protected = true;
43 private static bool _internal = false;
44 private static bool with_deps = false;
45 private static bool _private = false;
46 private static bool version = false;
47 private static bool use_svg_images = false;
49 private static bool disable_diagnostic_colors = false;
50 private static bool verbose = false;
51 private static bool force = false;
53 private static string basedir = null;
54 [CCode (array_length = false, array_null_terminated = true)]
55 private static string[] defines;
56 private static bool experimental;
57 private static bool experimental_non_null = false;
58 private static string profile;
59 [CCode (array_length = false, array_null_terminated = true)]
60 private static string[] import_packages;
61 [CCode (array_length = false, array_null_terminated = true)]
62 private static string[] import_directories;
63 [CCode (array_length = false, array_null_terminated = true)]
64 private static string[] vapi_directories;
65 [CCode (array_length = false, array_null_terminated = true)]
66 private static string[] metadata_directories;
67 [CCode (array_length = false, array_null_terminated = true)]
68 private static string[] gir_directories;
69 [CCode (array_length = false, array_null_terminated = true)]
70 private static string[] tsources;
71 [CCode (array_length = false, array_null_terminated = true)]
72 private static string[] packages;
73 [CCode (array_length = false, array_null_terminated = true)]
74 private static string[] alternative_resource_dirs;
75 static string target_glib;
77 private const GLib.OptionEntry[] options = {
78 { "directory", 'o', 0, OptionArg.FILENAME, ref directory, "Output directory", "DIRECTORY" },
80 { "basedir", 'b', 0, OptionArg.FILENAME, ref basedir, "Base source directory", "DIRECTORY" },
81 { "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, "Define SYMBOL", "SYMBOL..." },
82 { "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the default", "PROFILE" },
84 { "enable-experimental", 0, 0, OptionArg.NONE, ref experimental, "Enable experimental features", null },
85 { "enable-experimental-non-null", 0, 0, OptionArg.NONE, ref experimental_non_null, "Enable experimental enhancements for non-null types", null },
87 { "metadatadir", 0, 0, OptionArg.FILENAME_ARRAY, ref metadata_directories, "Look for GIR .metadata files in DIRECTORY", "DIRECTORY..." },
88 { "girdir", 0, 0, OptionArg.FILENAME_ARRAY, ref gir_directories, "Look for .gir files in DIRECTORY", "DIRECTORY..." },
89 { "vapidir", 0, 0, OptionArg.FILENAME_ARRAY, ref vapi_directories, "Look for package bindings in DIRECTORY", "DIRECTORY..." },
90 { "pkg", 0, 0, OptionArg.STRING_ARRAY, ref packages, "Include binding for PACKAGE", "PACKAGE..." },
92 { "driver", 0, 0, OptionArg.STRING, ref driverpath, "Name of an driver or path to a custom driver", null },
94 { "importdir", 0, 0, OptionArg.FILENAME_ARRAY, ref import_directories, "Look for external documentation in DIRECTORY", "DIRECTORY..." },
95 { "import", 0, 0, OptionArg.STRING_ARRAY, ref import_packages, "Include binding for PACKAGE", "PACKAGE..." },
96 { "alternative-resource-dir", 0, 0, OptionArg.STRING_ARRAY, ref alternative_resource_dirs, "Alternative resource directories", "DIRECTORY..." },
98 { "wiki", 0, 0, OptionArg.FILENAME, ref wikidirectory, "Wiki directory", "DIRECTORY" },
100 { "deps", 0, 0, OptionArg.NONE, ref with_deps, "Adds packages to the documentation", null },
102 { "doclet", 0, 0, OptionArg.STRING, ref docletpath, "Name of an included doclet or path to custom doclet", "PLUGIN"},
103 { "doclet-arg", 'X', 0, OptionArg.STRING_ARRAY, ref pluginargs, "Pass arguments to the doclet", "ARG" },
105 { "no-protected", 0, OptionFlags.REVERSE, OptionArg.NONE, ref _protected, "Removes protected elements from documentation", null },
106 { "internal", 0, 0, OptionArg.NONE, ref _internal, "Adds internal elements to documentation", null },
107 { "private", 0, 0, OptionArg.NONE, ref _private, "Adds private elements to documentation", null },
108 { "use-svg-images", 0, 0, OptionArg.NONE, ref use_svg_images, "Generate SVG image charts instead of PNG", null },
110 { "package-name", 0, 0, OptionArg.STRING, ref pkg_name, "package name", "NAME" },
111 { "package-version", 0, 0, OptionArg.STRING, ref pkg_version, "package version", "VERSION" },
112 { "gir", 0, 0, OptionArg.STRING, ref gir_name, "GObject-Introspection repository file name", "NAME-VERSION.gir" },
114 { "version", 0, 0, OptionArg.NONE, ref version, "Display version number", null },
116 { "force", 0, 0, OptionArg.NONE, ref force, "force", null },
117 { "verbose", 0, 0, OptionArg.NONE, ref verbose, "Show all warnings", null },
118 { "no-color", 0, 0, OptionArg.NONE, ref disable_diagnostic_colors, "Disable colored output", null },
119 { "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code generation", "MAJOR.MINOR" },
120 { OPTION_REMAINING, 0, 0, OptionArg.FILENAME_ARRAY, ref tsources, null, "FILE..." },
122 { null }
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;
135 private static bool check_pkg_name () {
136 if (pkg_name == null) {
137 return true;
140 if (pkg_name == "glib-2.0" || pkg_name == "gobject-2.0") {
141 return false;
144 foreach (string package in tsources) {
145 if (pkg_name == package) {
146 return false;
149 return true;
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);
161 return ValaDoc.pkg_name;
164 private ModuleLoader? create_module_loader (ErrorReporter reporter, out Doclet? doclet, out Driver? driver) {
165 ModuleLoader modules = ModuleLoader.get_instance ();
167 doclet = null;
168 driver = null;
170 // doclet:
171 string? pluginpath = ModuleLoader.get_doclet_path (docletpath, reporter);
172 if (pluginpath == null) {
173 return null;
176 doclet = modules.create_doclet (pluginpath);
177 if (doclet == null) {
178 reporter.simple_error (null, "failed to load doclet");
179 return null;
183 // driver:
184 driver = new Valadoc.Drivers.Driver ();
186 assert (driver != null && doclet != null);
188 return modules;
191 private int run (ErrorReporter reporter) {
192 // settings:
193 var settings = new Valadoc.Settings ();
194 reporter.settings = settings;
196 settings.pkg_name = this.get_pkg_name ();
197 settings.gir_namespace = ValaDoc.gir_namespace;
198 settings.gir_version = ValaDoc.gir_version;
199 if (ValaDoc.gir_name != null) {
200 settings.gir_name = GLib.Path.get_basename (ValaDoc.gir_name);
201 settings.gir_directory = GLib.Path.get_dirname (ValaDoc.gir_name);
202 if (settings.gir_directory == "") {
203 settings.gir_directory = GLib.Path.get_dirname (ValaDoc.directory);
206 settings.pkg_version = ValaDoc.pkg_version;
207 settings.add_inherited = ValaDoc.add_inherited;
208 settings._protected = ValaDoc._protected;
209 settings._internal = ValaDoc._internal;
210 settings.with_deps = ValaDoc.with_deps;
211 settings._private = ValaDoc._private;
212 settings.path = Vala.CodeContext.realpath (ValaDoc.directory);
213 settings.verbose = ValaDoc.verbose;
214 settings.wiki_directory = ValaDoc.wikidirectory;
215 settings.pluginargs = ValaDoc.pluginargs;
217 settings.experimental = experimental;
218 settings.experimental_non_null = experimental_non_null;
219 settings.basedir = basedir;
220 settings.directory = directory;
221 settings.vapi_directories = vapi_directories;
222 settings.metadata_directories = metadata_directories;
223 settings.gir_directories = gir_directories;
224 settings.target_glib = target_glib;
225 settings.use_svg_images = use_svg_images;
227 settings.source_files = tsources;
228 settings.packages = packages;
230 settings.profile = profile;
231 settings.defines = defines;
233 settings.alternative_resource_dirs = alternative_resource_dirs;
236 // load plugins:
237 Doclet? doclet = null;
238 Driver? driver = null;
240 ModuleLoader? modules = create_module_loader (reporter, out doclet, out driver);
241 if (reporter.errors > 0 || modules == null) {
242 return quit (reporter);
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);
254 // register child symbols:
255 Valadoc.Api.ChildSymbolRegistrar registrar = new Valadoc.Api.ChildSymbolRegistrar ();
256 doctree.accept (registrar);
258 // process documentation
259 Valadoc.DocumentationParser docparser = new Valadoc.DocumentationParser (settings, reporter, doctree, modules);
260 if (!doctree.create_tree()) {
261 return quit (reporter);
264 DocumentationImporter[] importers = {
265 new ValadocDocumentationImporter (doctree, docparser, modules, settings, reporter),
266 new GirDocumentationImporter (doctree, docparser, modules, settings, reporter)
269 doctree.parse_comments (docparser);
270 if (reporter.errors > 0) {
271 return quit (reporter);
274 doctree.import_comments (importers, import_packages, import_directories);
275 if (reporter.errors > 0) {
276 return quit (reporter);
279 doctree.check_comments (docparser);
280 if (reporter.errors > 0) {
281 return quit (reporter);
284 if (ValaDoc.gir_name != null) {
285 driver.write_gir (settings, reporter);
286 if (reporter.errors > 0) {
287 return quit (reporter);
291 doclet.process (settings, doctree, reporter);
292 return quit (reporter);
295 static int main (string[] args) {
296 Intl.setlocale (LocaleCategory.ALL, "");
297 ErrorReporter reporter = new ErrorReporter();
299 try {
300 var opt_context = new OptionContext ("- Vala Documentation Tool");
301 opt_context.set_help_enabled (true);
302 opt_context.add_main_entries (options, null);
303 opt_context.parse (ref args);
304 } catch (OptionError e) {
305 reporter.simple_error (null, "%s", e.message);
306 stdout.printf ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
307 return quit (reporter);
310 if (disable_diagnostic_colors == false) {
311 unowned string env_colors = Environment.get_variable ("VALA_COLORS");
312 if (env_colors != null) {
313 reporter.set_colors (env_colors);
314 } else {
315 reporter.set_colors (DEFAULT_COLORS);
319 if (version) {
320 stdout.printf ("Valadoc %s\n", Config.BUILD_VERSION);
321 return 0;
324 if (directory == null) {
325 reporter.simple_error (null, "No output directory specified.");
326 return quit (reporter);
329 if (!check_pkg_name ()) {
330 reporter.simple_error (null, "File already exists");
331 return quit (reporter);
334 if (FileUtils.test (directory, FileTest.EXISTS)) {
335 if (force == true) {
336 bool tmp = remove_directory (directory);
337 if (tmp == false) {
338 reporter.simple_error (null, "Can't remove directory.");
339 return quit (reporter);
341 } else {
342 reporter.simple_error (null, "File already exists");
343 return quit (reporter);
347 if (wikidirectory != null) {
348 if (!FileUtils.test(wikidirectory, FileTest.IS_DIR)) {
349 reporter.simple_error (null, "Wiki-directory does not exist.");
350 return quit (reporter);
354 foreach (unowned string dir in alternative_resource_dirs) {
355 if (!FileUtils.test(dir, FileTest.IS_DIR)) {
356 reporter.simple_error (null, "alternative resource directory '%s' does not exist.".printf (dir));
357 return quit (reporter);
360 if (reporter.errors > 0) {
361 return quit (reporter);
364 if (gir_name != null) {
365 long gir_len = gir_name.length;
366 int last_hyphen = gir_name.last_index_of_char ('-');
368 if (last_hyphen == -1 || !gir_name.has_suffix (".gir")) {
369 reporter.simple_error (null, "GIR file name '%s' is not well-formed, expected NAME-VERSION.gir", gir_name);
370 return quit (reporter);
373 gir_namespace = gir_name.substring (0, last_hyphen);
374 gir_version = gir_name.substring (last_hyphen + 1, gir_len - last_hyphen - 5);
375 gir_version.canon ("0123456789.", '?');
377 if (gir_namespace == "" || gir_version == "" || !gir_version[0].isdigit () || gir_version.contains ("?")) {
378 reporter.simple_error (null, "GIR file name '%s' is not well-formed, expected NAME-VERSION.gir", gir_name);
379 return quit (reporter);
383 bool report_warning = true;
384 foreach (string source in tsources) {
385 if (source.has_suffix (".vala") || source.has_suffix (".gs")) {
386 report_warning = false;
387 break;
391 if (report_warning == true) {
392 reporter.simple_error (null, "No source file specified to be compiled to gir.");
393 return quit (reporter);
398 var valadoc = new ValaDoc( );
399 return valadoc.run (reporter);