mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
compiler: @VMODULE
This commit is contained in:
parent
f67fca826e
commit
f9d5c0110f
25
doc/docs.md
25
doc/docs.md
@ -1274,16 +1274,31 @@ NB: For now you have to use one flag per line:
|
|||||||
|
|
||||||
You can also add C code, in your V module. For example, lets say that your C code is located in a folder named 'c' inside your module folder. Then:
|
You can also add C code, in your V module. For example, lets say that your C code is located in a folder named 'c' inside your module folder. Then:
|
||||||
|
|
||||||
|
* Put a v.mod file inside the toplevel folder of your module (if you
|
||||||
|
created your module with `v create` you already have v.mod file). For
|
||||||
|
example:
|
||||||
```v
|
```v
|
||||||
#flag -I @VMODULE/c
|
Module {
|
||||||
#flag @VMODULE/c/implementation.o
|
name: 'mymodule',
|
||||||
#include "header.h"
|
description: 'My nice module wraps a simple C library.',
|
||||||
|
version: '0.0.1'
|
||||||
|
dependencies: []
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
... will make V look for an compiled .o file in your module folder/c/implementation.o .
|
* Add these lines to the top of your module:
|
||||||
|
```v
|
||||||
|
#flag -I @VROOT/c
|
||||||
|
#flag @VROOT/c/implementation.o
|
||||||
|
#include "header.h"
|
||||||
|
```
|
||||||
|
NB: @VROOT will be replaced by V with the *nearest parent folder, where there is a v.mod file*.
|
||||||
|
|
||||||
|
The instructions above will make V look for an compiled .o file in your module folder/c/implementation.o .
|
||||||
If V finds it, the .o file will get linked to the main executable, that used the module.
|
If V finds it, the .o file will get linked to the main executable, that used the module.
|
||||||
If it does not find it, V assumes that there is a `@VMODULE/c/implementation.c` file,
|
If it does not find it, V assumes that there is a `@VROOT/c/implementation.c` file,
|
||||||
and tries to compile it to a .o file, then will use that.
|
and tries to compile it to a .o file, then will use that.
|
||||||
|
|
||||||
This allows you to have C code, that is contained in a V module, so that its distribution is easier.
|
This allows you to have C code, that is contained in a V module, so that its distribution is easier.
|
||||||
You can see a complete example for using C code in a V wrapper module here:
|
You can see a complete example for using C code in a V wrapper module here:
|
||||||
[minimal V project, that has a module, which contains C code](https://github.com/vlang/v/tree/master/vlib/compiler/tests/project_with_c_code)
|
[minimal V project, that has a module, which contains C code](https://github.com/vlang/v/tree/master/vlib/compiler/tests/project_with_c_code)
|
||||||
|
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
@ -14,6 +14,7 @@
|
|||||||
/fibonacci
|
/fibonacci
|
||||||
/sqlite
|
/sqlite
|
||||||
/path_tracing
|
/path_tracing
|
||||||
|
/gg2
|
||||||
*.ppm
|
*.ppm
|
||||||
empty_gg_freetype
|
empty_gg_freetype
|
||||||
game_of_life/life_gg
|
game_of_life/life_gg
|
||||||
|
8
v.mod
Normal file
8
v.mod
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#V Project#
|
||||||
|
|
||||||
|
Module {
|
||||||
|
name: 'V',
|
||||||
|
description: 'The V programming language.',
|
||||||
|
version: '0.1.25'
|
||||||
|
dependencies: []
|
||||||
|
}
|
@ -176,10 +176,11 @@ fn (v mut V) new_parser_from_file(path string) Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut p := v.new_parser(new_scanner_file(path))
|
mut p := v.new_parser(new_scanner_file(path))
|
||||||
|
path_dir := os.realpath(filepath.dir(path))
|
||||||
p = {
|
p = {
|
||||||
p |
|
p |
|
||||||
file_path:path,
|
file_path:path,
|
||||||
file_path_dir:filepath.dir(path),
|
file_path_dir: path_dir,
|
||||||
file_name:path.all_after(filepath.separator),
|
file_name:path.all_after(filepath.separator),
|
||||||
file_platform:path_platform,
|
file_platform:path_platform,
|
||||||
file_pcguard:path_pcguard,
|
file_pcguard:path_pcguard,
|
||||||
|
@ -242,12 +242,18 @@ fn (p mut Parser) chash() {
|
|||||||
if hash.starts_with('flag ') {
|
if hash.starts_with('flag ') {
|
||||||
if p.first_pass() {
|
if p.first_pass() {
|
||||||
mut flag := hash[5..]
|
mut flag := hash[5..]
|
||||||
// expand `@VROOT` `@VMOD` to absolute path
|
// expand `@VROOT` to its absolute path
|
||||||
flag = flag.replace('@VMODULE', p.file_path_dir)
|
if flag.contains('@VROOT') {
|
||||||
flag = flag.replace('@VROOT', p.vroot)
|
vmod_file_location := p.v.mod_file_cacher.get( p.file_path_dir )
|
||||||
flag = flag.replace('@VPATH', p.pref.vpath)
|
if vmod_file_location.vmod_file.len == 0 {
|
||||||
flag = flag.replace('@VLIB_PATH', p.pref.vlib_path)
|
// There was no actual v.mod file found.
|
||||||
flag = flag.replace('@VMOD', v_modules_path)
|
p.error_with_token_index('To use @VROOT, you need' +
|
||||||
|
' to have a "v.mod" file in ${p.file_path_dir},' +
|
||||||
|
' or in one of its parent folders.',
|
||||||
|
p.cur_tok_index() - 1)
|
||||||
|
}
|
||||||
|
flag = flag.replace('@VROOT', vmod_file_location.vmod_folder )
|
||||||
|
}
|
||||||
// p.log('adding flag "$flag"')
|
// p.log('adding flag "$flag"')
|
||||||
_ = p.table.parse_cflag(flag, p.mod, p.v.pref.compile_defines_all ) or {
|
_ = p.table.parse_cflag(flag, p.mod, p.v.pref.compile_defines_all ) or {
|
||||||
p.error_with_token_index(err, p.cur_tok_index() - 1)
|
p.error_with_token_index(err, p.cur_tok_index() - 1)
|
||||||
@ -561,4 +567,3 @@ pub fn (e &$typ.name) has(flag $typ.name) bool { return int(*e)&(1 << int(flag))
|
|||||||
p.cgen.fns << 'void ${typ.name}_toggle($typ.name *e, $typ.name flag);'
|
p.cgen.fns << 'void ${typ.name}_toggle($typ.name *e, $typ.name flag);'
|
||||||
p.cgen.fns << 'bool ${typ.name}_has($typ.name *e, $typ.name flag);'
|
p.cgen.fns << 'bool ${typ.name}_has($typ.name *e, $typ.name flag);'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ enum Pass {
|
|||||||
|
|
||||||
pub struct V {
|
pub struct V {
|
||||||
pub mut:
|
pub mut:
|
||||||
|
mod_file_cacher &ModFileCacher // used during lookup for v.mod to support @VROOT
|
||||||
out_name_c string // name of the temporary C file
|
out_name_c string // name of the temporary C file
|
||||||
files []string // all V files that need to be parsed and compiled
|
files []string // all V files that need to be parsed and compiled
|
||||||
compiled_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
|
compiled_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
|
||||||
@ -68,6 +69,7 @@ pub fn new_v(pref &pref.Preferences) &V {
|
|||||||
compiled_dir:=if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
|
compiled_dir:=if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
|
||||||
|
|
||||||
return &V{
|
return &V{
|
||||||
|
mod_file_cacher: new_mod_file_cacher()
|
||||||
compiled_dir:compiled_dir// if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
|
compiled_dir:compiled_dir// if os.is_dir(rdir) { rdir } else { filepath.dir(rdir) }
|
||||||
table: new_table(pref.obfuscate)
|
table: new_table(pref.obfuscate)
|
||||||
out_name_c: out_name_c
|
out_name_c: out_name_c
|
||||||
@ -815,8 +817,8 @@ pub fn (v mut V) parse_lib_imports() {
|
|||||||
if mod in done_imports {
|
if mod in done_imports {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
import_path := v.find_module_path(mod) or {
|
import_path := v.parsers[i].find_module_path(mod) or {
|
||||||
v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_table.get_import_tok_idx(mod))
|
v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)\n$err', v.parsers[i].import_table.get_import_tok_idx(mod))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
vfiles := v.v_files_from_dir(import_path)
|
vfiles := v.v_files_from_dir(import_path)
|
||||||
|
@ -182,21 +182,21 @@ fn (v mut V) set_module_lookup_paths() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (v &V) find_module_path(mod string) ?string {
|
fn (p &Parser) find_module_path(mod string) ?string {
|
||||||
mod_path := v.module_path(mod)
|
mod_path := p.v.module_path(mod)
|
||||||
for lookup_path in v.module_lookup_paths {
|
for lookup_path in p.v.module_lookup_paths {
|
||||||
try_path := filepath.join(lookup_path,mod_path)
|
try_path := filepath.join(lookup_path,mod_path)
|
||||||
if v.pref.is_verbose {
|
if p.v.pref.is_verbose {
|
||||||
println(' >> trying to find $mod in $try_path ...')
|
println(' >> trying to find $mod in $try_path ...')
|
||||||
}
|
}
|
||||||
if os.is_dir(try_path) {
|
if os.is_dir(try_path) {
|
||||||
if v.pref.is_verbose {
|
if p.v.pref.is_verbose {
|
||||||
println(' << found $try_path .')
|
println(' << found $try_path .')
|
||||||
}
|
}
|
||||||
return try_path
|
return try_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return error('module "$mod" not found')
|
return error('module "$mod" not found in ${p.v.module_lookup_paths}')
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
|
4
vlib/compiler/tests/project_with_c_code/.v.mod.stop
Normal file
4
vlib/compiler/tests/project_with_c_code/.v.mod.stop
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Do not delete this file.
|
||||||
|
It is used by V to stop the lookup for v.mod,
|
||||||
|
so that the top level vlib/v.mod is not found,
|
||||||
|
if you delete mod1/v.mod .
|
7
vlib/compiler/tests/project_with_c_code/mod1/v.mod
Normal file
7
vlib/compiler/tests/project_with_c_code/mod1/v.mod
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#V Module#
|
||||||
|
|
||||||
|
Module {
|
||||||
|
name: 'mod1',
|
||||||
|
description: 'A simple module, containing C code.',
|
||||||
|
dependencies: []
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
module mod1
|
module mod1
|
||||||
|
|
||||||
#flag -I @VMODULE/c
|
#flag -I @VROOT/c
|
||||||
#flag @VMODULE/c/implementation.o
|
#flag @VROOT/c/implementation.o
|
||||||
|
|
||||||
#include "header.h"
|
#include "header.h"
|
||||||
|
|
||||||
|
153
vlib/compiler/v_mod_cache.v
Normal file
153
vlib/compiler/v_mod_cache.v
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
module compiler
|
||||||
|
|
||||||
|
import os
|
||||||
|
import filepath
|
||||||
|
|
||||||
|
|
||||||
|
// This file provides a caching mechanism for seeking quickly whether a
|
||||||
|
// given folder has a v.mod file in it or in any of its parent folders.
|
||||||
|
//
|
||||||
|
// ModFileCacher.get(folder) works in such a way, that given this tree:
|
||||||
|
// examples/hanoi.v
|
||||||
|
// vlib/v.mod
|
||||||
|
// vlib/compiler/tests/project_with_c_code/mod1/v.mod
|
||||||
|
// vlib/compiler/tests/project_with_c_code/mod1/wrapper.v
|
||||||
|
// -----------------
|
||||||
|
// ModFileCacher.get('examples')
|
||||||
|
// => ModFileAndFolder{'', 'examples'}
|
||||||
|
// ModFileCacher.get('vlib/compiler/tests')
|
||||||
|
// => ModFileAndFolder{'vlib/v.mod', 'vlib'}
|
||||||
|
// ModFileCacher.get('vlib/compiler')
|
||||||
|
// => ModFileAndFolder{'vlib/v.mod', 'vlib'}
|
||||||
|
// ModFileCacher.get('vlib/project_with_c_code/mod1')
|
||||||
|
// => ModFileAndFolder{'vlib/project_with_c_code/mod1/v.mod', 'vlib/project_with_c_code/mod1'}
|
||||||
|
|
||||||
|
|
||||||
|
struct ModFileAndFolder {
|
||||||
|
// vmod_file contains the full path of the found 'v.mod' file, or ''
|
||||||
|
// if no 'v.mod' file was found in file_path_dir, or in its parent folders.
|
||||||
|
vmod_file string
|
||||||
|
|
||||||
|
// vmod_folder contains the file_path_dir, if there is no 'v.mod' file in
|
||||||
|
// *any* of the parent folders, otherwise it is the first parent folder,
|
||||||
|
// where a v.mod file was found.
|
||||||
|
vmod_folder string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModFileCacher {
|
||||||
|
mut:
|
||||||
|
cache map[string]ModFileAndFolder
|
||||||
|
// folder_files caches os.ls(key)
|
||||||
|
folder_files map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_mod_file_cacher() &ModFileCacher {
|
||||||
|
return &ModFileCacher{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mcache &ModFileCacher) dump() {
|
||||||
|
$if debug {
|
||||||
|
eprintln('ModFileCacher DUMP:')
|
||||||
|
eprintln(' ModFileCacher.cache:')
|
||||||
|
for k,v in mcache.cache {
|
||||||
|
eprintln(' K: ${k:-32s} | V: "${v.vmod_file:32s}" | "${v.vmod_folder:32s}" ')
|
||||||
|
}
|
||||||
|
eprintln(' ModFileCacher.folder_files:')
|
||||||
|
for k,v in mcache.folder_files {
|
||||||
|
eprintln(' K: ${k:-32s} | V: ${v.str()}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mcache mut ModFileCacher) get(mfolder string) ModFileAndFolder {
|
||||||
|
if mfolder in mcache.cache {
|
||||||
|
return mcache.cache[ mfolder ]
|
||||||
|
}
|
||||||
|
traversed_folders, res := mcache.traverse( mfolder )
|
||||||
|
for tfolder in traversed_folders {
|
||||||
|
mcache.add( tfolder, res )
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (cacher mut ModFileCacher) add(path string, result ModFileAndFolder) {
|
||||||
|
cacher.cache[ path ] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mcache mut ModFileCacher) traverse(mfolder string) ([]string, ModFileAndFolder) {
|
||||||
|
mut cfolder := mfolder
|
||||||
|
mut folders_so_far := [cfolder]
|
||||||
|
mut levels := 0
|
||||||
|
for {
|
||||||
|
$if debug {
|
||||||
|
eprintln('pdir2vmod mfolder: ${mfolder:-32s} | cfolder: ${cfolder:-20s} | levels: $levels')
|
||||||
|
}
|
||||||
|
if levels > 255 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if cfolder == '/' || cfolder == '' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if cfolder in mcache.cache {
|
||||||
|
res := mcache.cache[ cfolder ]
|
||||||
|
if res.vmod_file.len == 0 {
|
||||||
|
mcache.mark_folders_as_vmod_free( folders_so_far )
|
||||||
|
}else{
|
||||||
|
mcache.mark_folders_with_vmod( folders_so_far, res )
|
||||||
|
}
|
||||||
|
return []string, res
|
||||||
|
}
|
||||||
|
files := mcache.get_files( cfolder )
|
||||||
|
if 'v.mod' in files {
|
||||||
|
// TODO: actually read the v.mod file and parse its contents to see
|
||||||
|
// if its source folder is different
|
||||||
|
res := ModFileAndFolder{ vmod_file: filepath.join( cfolder, 'v.mod'), vmod_folder: cfolder }
|
||||||
|
$if debug {
|
||||||
|
eprintln('FOUND v.mod:')
|
||||||
|
eprintln(' ModFileAndFolder{ vmod_file: $res.vmod_file , vmod_folder: $res.vmod_folder } ')
|
||||||
|
}
|
||||||
|
return folders_so_far, res
|
||||||
|
}
|
||||||
|
if mcache.check_for_stop( cfolder, files ) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cfolder = filepath.basedir( cfolder )
|
||||||
|
folders_so_far << cfolder
|
||||||
|
levels++
|
||||||
|
}
|
||||||
|
mcache.mark_folders_as_vmod_free( folders_so_far )
|
||||||
|
return [mfolder], ModFileAndFolder{ vmod_file: '', vmod_folder: mfolder }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mcache mut ModFileCacher) mark_folders_with_vmod( folders_so_far []string, vmod ModFileAndFolder ) {
|
||||||
|
for f in folders_so_far {
|
||||||
|
mcache.add( f, vmod )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mcache mut ModFileCacher) mark_folders_as_vmod_free( folders_so_far []string ) {
|
||||||
|
// No need to check these folders anymore,
|
||||||
|
// because their parents do not contain v.mod files
|
||||||
|
for f in folders_so_far {
|
||||||
|
mcache.add( f, ModFileAndFolder{ vmod_file: '', vmod_folder: f } )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ( MOD_FILE_STOP_PATHS = ['.git', '.hg', '.svn', '.v.mod.stop' ] )
|
||||||
|
fn (mcache &ModFileCacher) check_for_stop(cfolder string, files []string) bool {
|
||||||
|
for i in MOD_FILE_STOP_PATHS {
|
||||||
|
if i in files {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mcache mut ModFileCacher) get_files(cfolder string) []string {
|
||||||
|
if cfolder in mcache.folder_files {
|
||||||
|
return mcache.folder_files[ cfolder ]
|
||||||
|
}
|
||||||
|
files := os.ls(cfolder) or { return [] }
|
||||||
|
mcache.folder_files[ cfolder ] = files
|
||||||
|
return files
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user