298 lines
7.8 KiB
V
298 lines
7.8 KiB
V
module builder
|
|
|
|
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
|
|
import v.gen
|
|
import v.gen.js
|
|
import v.gen.x64
|
|
import v.depgraph
|
|
|
|
pub struct Builder {
|
|
pub:
|
|
table &table.Table
|
|
checker checker.Checker
|
|
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
|
|
mut:
|
|
pref &pref.Preferences
|
|
module_search_paths []string
|
|
parsed_files []ast.File
|
|
global_scope &ast.Scope
|
|
out_name_c string
|
|
out_name_js string
|
|
}
|
|
|
|
pub fn new_builder(pref &pref.Preferences) Builder {
|
|
rdir := os.real_path(pref.path)
|
|
compiled_dir := if os.is_dir(rdir) { rdir } else { os.dir(rdir) }
|
|
table := table.new_table()
|
|
return Builder{
|
|
pref: pref
|
|
table: table
|
|
checker: checker.new_checker(table, pref)
|
|
global_scope: &ast.Scope{
|
|
parent: 0
|
|
}
|
|
compiled_dir: compiled_dir
|
|
}
|
|
}
|
|
|
|
// parse all deps from already parsed files
|
|
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.
|
|
for i := 0; i < b.parsed_files.len; i++ {
|
|
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
|
|
// println('module_search_paths:')
|
|
// println(b.module_search_paths)
|
|
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))
|
|
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)
|
|
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`')
|
|
}
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
|
|
pub fn (mut b Builder) resolve_deps() {
|
|
graph := b.import_graph()
|
|
deps_resolved := graph.resolve()
|
|
if !deps_resolved.acyclic {
|
|
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
|
|
// 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 {
|
|
mut builtins := util.builtin_module_parts
|
|
builtins << 'builtin'
|
|
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
|
|
}
|
|
|
|
pub fn (b Builder) v_files_from_dir(dir string) []string {
|
|
mut res := []string{}
|
|
if !os.exists(dir) {
|
|
if dir == 'compiler' && os.is_dir('vlib') {
|
|
println('looks like you are trying to build V with an old command')
|
|
println('use `v -o v cmd/v` instead of `v -o v compiler`')
|
|
}
|
|
verror("$dir doesn't exist")
|
|
} else if !os.is_dir(dir) {
|
|
verror("$dir isn't a directory!")
|
|
}
|
|
mut files := os.ls(dir) or {
|
|
panic(err)
|
|
}
|
|
if b.pref.is_verbose {
|
|
println('v_files_from_dir ("$dir")')
|
|
}
|
|
files.sort()
|
|
for file in files {
|
|
if !file.ends_with('.v') && !file.ends_with('.vh') {
|
|
continue
|
|
}
|
|
if file.ends_with('_test.v') {
|
|
continue
|
|
}
|
|
if b.pref.backend == .c && !b.should_compile_c(file) {
|
|
continue
|
|
}
|
|
if b.pref.backend == .js && !b.should_compile_js(file) {
|
|
continue
|
|
}
|
|
if b.pref.compile_defines_all.len > 0 && file.contains('_d_') {
|
|
mut allowed := false
|
|
for cdefine in b.pref.compile_defines {
|
|
file_postfix := '_d_${cdefine}.v'
|
|
if file.ends_with(file_postfix) {
|
|
allowed = true
|
|
break
|
|
}
|
|
}
|
|
if !allowed {
|
|
continue
|
|
}
|
|
}
|
|
res << os.join_path(dir, file)
|
|
}
|
|
return res
|
|
}
|
|
|
|
[inline]
|
|
fn (b Builder) should_compile_c(file string) bool {
|
|
if !file.ends_with('.c.v') && file.split('.').len > 2 {
|
|
// Probably something like `a.js.v`.
|
|
return false
|
|
}
|
|
if file.ends_with('_windows.c.v') && b.pref.os != .windows {
|
|
return false
|
|
}
|
|
if file.ends_with('_linux.c.v') && b.pref.os != .linux {
|
|
return false
|
|
}
|
|
if file.ends_with('_darwin.c.v') && b.pref.os != .mac {
|
|
return false
|
|
}
|
|
if file.ends_with('_nix.c.v') && b.pref.os == .windows {
|
|
return false
|
|
}
|
|
if file.ends_with('_android.c.v') && b.pref.os != .android {
|
|
return false
|
|
}
|
|
if file.ends_with('_freebsd.c.v') && b.pref.os != .freebsd {
|
|
return false
|
|
}
|
|
if file.ends_with('_solaris.c.v') && b.pref.os != .solaris {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
[inline]
|
|
fn (b Builder) should_compile_js(file string) bool {
|
|
if !file.ends_with('.js.v') && file.split('.').len > 2 {
|
|
// Probably something like `a.c.v`.
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
pub fn (b Builder) log(s string) {
|
|
if b.pref.is_verbose {
|
|
println(s)
|
|
}
|
|
}
|
|
|
|
pub fn (b Builder) info(s string) {
|
|
if b.pref.is_verbose {
|
|
println(s)
|
|
}
|
|
}
|
|
|
|
[inline]
|
|
fn module_path(mod string) string {
|
|
// submodule support
|
|
return mod.replace('.', os.path_separator)
|
|
}
|
|
|
|
pub fn (b Builder) find_module_path(mod, fpath string) ?string {
|
|
// support @VROOT/v.mod relative paths:
|
|
vmod_file_location := vmod.mod_file_cacher.get(fpath)
|
|
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 {
|
|
try_path := os.join_path(search_path, mod_path)
|
|
if b.pref.is_verbose {
|
|
println(' >> trying to find $mod in $try_path ..')
|
|
}
|
|
if os.is_dir(try_path) {
|
|
if b.pref.is_verbose {
|
|
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')
|
|
}
|
|
|
|
fn (b &Builder) print_warnings_and_errors(){
|
|
if b.checker.nr_warnings > 0 {
|
|
for 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)
|
|
}
|
|
}
|
|
if b.checker.nr_errors > 0 {
|
|
for 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)
|
|
}
|
|
exit(1)
|
|
}
|
|
}
|
|
|
|
fn verror(s string) {
|
|
util.verror('builder error', s)
|
|
}
|