v/vlib/v/builder/builder.v

257 lines
6.8 KiB
V
Raw Normal View History

module builder
2020-04-16 15:35:19 +02:00
import os
import time
import v.ast
import v.table
import v.pref
import v.util
import v.vmod
import v.checker
import v.parser
import v.errors
2020-04-16 15:35:19 +02:00
import v.gen
import v.gen.js
import v.gen.x64
import v.depgraph
2020-05-09 15:16:48 +02:00
const (
max_nr_errors = 100
)
pub struct Builder {
pub:
table &table.Table
checker checker.Checker
2020-03-20 16:41:18 +01:00
compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
module_path string
2020-02-03 07:31:54 +01:00
mut:
pref &pref.Preferences
parsed_files []ast.File
global_scope &ast.Scope
2020-04-07 00:44:19 +02:00
out_name_c string
2020-04-16 15:35:19 +02:00
out_name_js string
2020-05-09 18:34:04 +02:00
pub mut:
module_search_paths []string
}
pub fn new_builder(pref &pref.Preferences) Builder {
2020-04-07 00:44:19 +02:00
rdir := os.real_path(pref.path)
compiled_dir := if os.is_dir(rdir) { rdir } else { os.dir(rdir) }
table := table.new_table()
2020-04-16 15:35:19 +02:00
return Builder{
pref: pref
table: table
checker: checker.new_checker(table, pref)
global_scope: &ast.Scope{
parent: 0
}
2020-04-07 00:44:19 +02:00
compiled_dir: compiled_dir
}
}
2020-02-03 07:31:54 +01:00
// parse all deps from already parsed files
2020-04-25 17:49:16 +02:00
pub fn (mut b Builder) parse_imports() {
mut done_imports := []string{}
// NB: b.parsed_files is appended in the loop,
// so we can not use the shorter `for in` form.
2020-04-16 15:35:19 +02:00
for i := 0; i < b.parsed_files.len; i++ {
2020-02-03 07:31:54 +01:00
ast_file := b.parsed_files[i]
for _, imp in ast_file.imports {
mod := imp.mod
if mod in done_imports {
continue
}
import_path := b.find_module_path(mod, ast_file.path) or {
// v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_table.get_import_tok_idx(mod))
// break
2020-03-01 21:56:07 +01:00
// println('module_search_paths:')
// println(b.module_search_paths)
2020-02-03 07:31:54 +01:00
panic('cannot import module "$mod" (not found)')
}
v_files := b.v_files_from_dir(import_path)
if v_files.len == 0 {
// v.parsers[i].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', v.parsers[i].import_table.get_import_tok_idx(mod))
2020-02-03 07:31:54 +01:00
panic('cannot import module "$mod" (no .v files in "$import_path")')
}
// Add all imports referenced by these libs
parsed_files := parser.parse_files(v_files, b.table, b.pref, b.global_scope)
2020-02-03 07:31:54 +01:00
for file in parsed_files {
if file.mod.name != mod {
// v.parsers[pidx].error_with_token_index('bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1
verror('bad module definition: ${ast_file.path} imports module "$mod" but $file.path is defined as module `$file.mod.name`')
2020-02-03 07:31:54 +01:00
}
}
b.parsed_files << parsed_files
done_imports << mod
}
}
b.resolve_deps()
//
if b.pref.print_v_files {
for p in b.parsed_files {
println(p.path)
}
exit(0)
}
}
2020-04-25 17:49:16 +02:00
pub fn (mut b Builder) resolve_deps() {
graph := b.import_graph()
deps_resolved := graph.resolve()
if !deps_resolved.acyclic {
2020-04-16 15:35:19 +02:00
eprintln('warning: import cycle detected between the following modules: \n' + deps_resolved.display_cycles())
// TODO: error, when v itself does not have v.table -> v.ast -> v.table cycles anymore
return
}
if b.pref.is_verbose {
eprintln('------ resolved dependencies graph: ------')
eprintln(deps_resolved.display())
eprintln('------------------------------------------')
}
mut mods := []string{}
for node in deps_resolved.nodes {
mods << node.name
}
if b.pref.is_verbose {
eprintln('------ imported modules: ------')
eprintln(mods.str())
eprintln('-------------------------------')
}
mut reordered_parsed_files := []ast.File{}
for m in mods {
for pf in b.parsed_files {
if m == pf.mod.name {
reordered_parsed_files << pf
2020-04-16 15:35:19 +02:00
// eprintln('pf.mod.name: $pf.mod.name | pf.path: $pf.path')
}
}
}
b.parsed_files = reordered_parsed_files
}
// graph of all imported modules
pub fn (b &Builder) import_graph() &depgraph.DepGraph {
2020-04-23 01:16:58 +02:00
mut builtins := util.builtin_module_parts
builtins << 'builtin'
2020-04-23 01:16:58 +02:00
mut graph := depgraph.new_dep_graph()
for p in b.parsed_files {
mut deps := []string{}
if p.mod.name !in builtins {
deps << 'builtin'
}
for _, m in p.imports {
deps << m.mod
}
graph.add(p.mod.name, deps)
}
return graph
2020-02-03 07:31:54 +01:00
}
2020-04-07 00:44:19 +02:00
pub fn (b Builder) v_files_from_dir(dir string) []string {
2020-02-03 07:31:54 +01:00
if !os.exists(dir) {
if dir == 'compiler' && os.is_dir('vlib') {
println('looks like you are trying to build V with an old command')
2020-02-09 10:08:04 +01:00
println('use `v -o v cmd/v` instead of `v -o v compiler`')
2020-02-03 07:31:54 +01:00
}
verror("$dir doesn't exist")
2020-04-07 00:44:19 +02:00
} else if !os.is_dir(dir) {
2020-03-24 11:14:11 +01:00
verror("$dir isn't a directory!")
2020-02-03 07:31:54 +01:00
}
2020-04-23 01:16:58 +02:00
mut files := os.ls(dir) or {
2020-02-03 07:31:54 +01:00
panic(err)
}
2020-04-07 00:44:19 +02:00
if b.pref.is_verbose {
2020-02-03 07:31:54 +01:00
println('v_files_from_dir ("$dir")')
}
return b.pref.should_compile_filtered_files(dir, files)
}
2020-04-07 00:44:19 +02:00
pub fn (b Builder) log(s string) {
if b.pref.is_verbose {
2020-02-03 07:31:54 +01:00
println(s)
}
}
2020-03-01 21:56:07 +01:00
2020-04-07 00:44:19 +02:00
pub fn (b Builder) info(s string) {
if b.pref.is_verbose {
2020-03-31 19:58:44 +02:00
println(s)
}
}
2020-03-01 21:56:07 +01:00
[inline]
fn module_path(mod string) string {
// submodule support
2020-03-07 22:26:26 +01:00
return mod.replace('.', os.path_separator)
2020-03-01 21:56:07 +01:00
}
2020-04-16 15:35:19 +02:00
pub fn (b Builder) find_module_path(mod, fpath string) ?string {
// support @VROOT/v.mod relative paths:
2020-04-16 15:35:19 +02:00
vmod_file_location := vmod.mod_file_cacher.get(fpath)
2020-03-01 21:56:07 +01:00
mod_path := module_path(mod)
mut module_lookup_paths := []string{}
if vmod_file_location.vmod_file.len != 0 && vmod_file_location.vmod_folder !in b.module_search_paths {
module_lookup_paths << vmod_file_location.vmod_folder
}
module_lookup_paths << b.module_search_paths
for search_path in module_lookup_paths {
2020-04-07 00:44:19 +02:00
try_path := os.join_path(search_path, mod_path)
if b.pref.is_verbose {
2020-03-01 21:56:07 +01:00
println(' >> trying to find $mod in $try_path ..')
}
if os.is_dir(try_path) {
2020-04-07 00:44:19 +02:00
if b.pref.is_verbose {
2020-03-01 21:56:07 +01:00
println(' << found $try_path .')
}
return try_path
}
}
smodule_lookup_paths := module_lookup_paths.join(', ')
return error('module "$mod" not found in:\n$smodule_lookup_paths')
2020-03-01 21:56:07 +01:00
}
fn (b &Builder) print_warnings_and_errors() {
if b.pref.output_mode == .silent {
if b.checker.nr_errors > 0 {
exit(1)
}
return
}
2020-05-09 15:16:48 +02:00
if b.pref.is_verbose && b.checker.nr_warnings > 1 {
println('$b.checker.nr_warnings warnings')
}
if b.checker.nr_warnings > 0 {
2020-05-09 15:16:48 +02:00
for i, err in b.checker.warnings {
kind := if b.pref.is_verbose { '$err.reporter warning #$b.checker.nr_warnings:' } else { 'warning:' }
ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
eprintln(ferror)
2020-05-09 15:16:48 +02:00
// eprintln('')
if i > max_nr_errors {
return
}
}
2020-04-13 01:56:01 +02:00
}
2020-05-09 15:16:48 +02:00
//
if b.pref.is_verbose && b.checker.nr_errors > 1 {
println('$b.checker.nr_errors errors')
}
if b.checker.nr_errors > 0 {
2020-05-09 15:16:48 +02:00
for i, err in b.checker.errors {
kind := if b.pref.is_verbose { '$err.reporter error #$b.checker.nr_errors:' } else { 'error:' }
ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
eprintln(ferror)
2020-05-09 15:16:48 +02:00
// eprintln('')
if i > max_nr_errors {
return
}
}
exit(1)
}
}
fn verror(s string) {
util.verror('builder error', s)
}