249 lines
6.4 KiB
V
249 lines
6.4 KiB
V
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
||
// Use of this source code is governed by an MIT license
|
||
// that can be found in the LICENSE file.
|
||
module util
|
||
|
||
import (
|
||
os
|
||
v.pref
|
||
)
|
||
|
||
pub const (
|
||
v_version = '0.1.26'
|
||
)
|
||
|
||
pub const (
|
||
builtin_module_parts = ['math.bits' /* needed by strconv.ftoa */, 'strconv', 'strconv.ftoa', 'hash.wyhash', 'strings']
|
||
)
|
||
|
||
// vhash() returns the build string C.V_COMMIT_HASH . See cmd/tools/gen_vc.v .
|
||
pub fn vhash() string {
|
||
mut buf := [50]byte
|
||
buf[0] = 0
|
||
C.snprintf(charptr(buf), 50, '%s', C.V_COMMIT_HASH)
|
||
return tos_clone(buf)
|
||
}
|
||
|
||
pub fn full_hash() string {
|
||
build_hash := vhash()
|
||
current_hash := githash(false)
|
||
if build_hash == current_hash {
|
||
return build_hash
|
||
}
|
||
return '${build_hash}.${current_hash}'
|
||
}
|
||
|
||
// full_v_version() returns the full version of the V compiler
|
||
pub fn full_v_version() string {
|
||
return 'V ${v_version} ${full_hash()}'
|
||
}
|
||
|
||
// githash(x) returns the current git commit hash.
|
||
// When x is false, it is very fast - it just returns a predefined C constant.
|
||
// When x is true, it tries to get the current commit hash, by parsing the
|
||
// relevant files in the .git/ folder, or if that is not possible
|
||
// for example when using a V from a V binary release, that does not have .git/
|
||
// defaults to getting the predefined C constant again.
|
||
// NB: githash(true) must be called only when v detects that it builds itself.
|
||
// For all other programs, githash(false) should be used.
|
||
pub fn githash(should_get_from_filesystem bool) string {
|
||
for {
|
||
// The `for` construct here is used as a goto substitute.
|
||
// The code in this function will break out of the `for`
|
||
// if it detects an error and can not continue.
|
||
if should_get_from_filesystem {
|
||
vexe := os.getenv('VEXE')
|
||
vroot := os.dir(vexe)
|
||
// .git/HEAD
|
||
git_head_file := os.join_path(vroot, '.git', 'HEAD')
|
||
if !os.exists(git_head_file) {
|
||
break
|
||
}
|
||
// 'ref: refs/heads/master' ... the current branch name
|
||
head_content := os.read_file(git_head_file) or {
|
||
break
|
||
}
|
||
gcbranch_rel_path := head_content.replace('ref: ', '').trim_space()
|
||
gcbranch_file := os.join_path(vroot, '.git', gcbranch_rel_path)
|
||
// .git/refs/heads/master
|
||
if !os.exists(gcbranch_file) {
|
||
break
|
||
}
|
||
// get the full commit hash contained in the ref heads file
|
||
current_branch_hash := os.read_file(gcbranch_file) or {
|
||
break
|
||
}
|
||
desired_hash_length := 7
|
||
if current_branch_hash.len > desired_hash_length {
|
||
return current_branch_hash[0..desired_hash_length]
|
||
}
|
||
}
|
||
break
|
||
}
|
||
mut buf := [50]byte
|
||
buf[0] = 0
|
||
C.snprintf(charptr(buf), 50, '%s', C.V_CURRENT_COMMIT_HASH)
|
||
return tos_clone(buf)
|
||
}
|
||
|
||
//
|
||
fn set_vroot_folder(vroot_path string) {
|
||
// Preparation for the compiler module:
|
||
// VEXE env variable is needed so that compiler.vexe_path()
|
||
// can return it later to whoever needs it:
|
||
vname := if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
|
||
os.setenv('VEXE', os.real_path(os.join_path(vroot_path, vname)), true)
|
||
}
|
||
|
||
pub fn launch_tool(is_verbose bool, tool_name string) {
|
||
vexe := pref.vexe_path()
|
||
vroot := os.dir(vexe)
|
||
set_vroot_folder(vroot)
|
||
tool_args := os.args[1..].join(' ')
|
||
tool_exe := path_of_executable(os.real_path('$vroot/cmd/tools/$tool_name'))
|
||
tool_source := os.real_path('$vroot/cmd/tools/${tool_name}.v')
|
||
tool_command := '"$tool_exe" $tool_args'
|
||
if is_verbose {
|
||
println('launch_tool vexe : $vroot')
|
||
println('launch_tool vroot : $vroot')
|
||
println('launch_tool tool_args : $tool_args')
|
||
println('launch_tool tool_command: $tool_command')
|
||
}
|
||
// TODO Caching should be done on the `vlib/v` level.
|
||
mut should_compile := false
|
||
if !os.exists(tool_exe) {
|
||
should_compile = true
|
||
} else {
|
||
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(vexe) {
|
||
// v was recompiled, maybe after v up ...
|
||
// rebuild the tool too just in case
|
||
should_compile = true
|
||
if tool_name == 'vself' || tool_name == 'vup' {
|
||
// The purpose of vself/up is to update and recompile v itself.
|
||
// After the first 'v self' execution, v will be modified, so
|
||
// then a second 'v self' will detect, that v is newer than the
|
||
// vself executable, and try to recompile vself/up again, which
|
||
// will slow down the next v recompilation needlessly.
|
||
should_compile = false
|
||
}
|
||
}
|
||
if os.file_last_mod_unix(tool_exe) <= os.file_last_mod_unix(tool_source) {
|
||
// the user changed the source code of the tool, or git updated it:
|
||
should_compile = true
|
||
}
|
||
}
|
||
if is_verbose {
|
||
println('launch_tool should_compile: $should_compile')
|
||
}
|
||
if should_compile {
|
||
mut compilation_command := '"$vexe" '
|
||
compilation_command += '"$tool_source"'
|
||
if is_verbose {
|
||
println('Compiling $tool_name with: "$compilation_command"')
|
||
}
|
||
tool_compilation := os.exec(compilation_command) or {
|
||
panic(err)
|
||
}
|
||
if tool_compilation.exit_code != 0 {
|
||
mut err := 'Permission denied'
|
||
if !tool_compilation.output.contains('Permission denied') {
|
||
err = '\n$tool_compilation.output'
|
||
}
|
||
eprintln('cannot compile ‘$tool_source: $err‘')
|
||
exit(1)
|
||
}
|
||
}
|
||
if is_verbose {
|
||
println('launch_tool running tool command: $tool_command ...')
|
||
}
|
||
exit(os.system(tool_command))
|
||
}
|
||
|
||
pub fn path_of_executable(path string) string {
|
||
$if windows {
|
||
return path + '.exe'
|
||
}
|
||
return path
|
||
}
|
||
|
||
pub fn read_file(file_path string) ?string {
|
||
mut raw_text := os.read_file(file_path) or {
|
||
return error('failed to open $file_path')
|
||
}
|
||
// BOM check
|
||
if raw_text.len >= 3 {
|
||
c_text := raw_text.str
|
||
if c_text[0] == 0xEF && c_text[1] == 0xBB && c_text[2] == 0xBF {
|
||
// skip three BOM bytes
|
||
offset_from_begin := 3
|
||
raw_text = tos(c_text[offset_from_begin], vstrlen(c_text) - offset_from_begin)
|
||
}
|
||
}
|
||
return raw_text
|
||
}
|
||
|
||
[inline]
|
||
fn imin(a, b int) int {
|
||
return if a < b {
|
||
a
|
||
} else {
|
||
b
|
||
}
|
||
}
|
||
|
||
[inline]
|
||
fn imax(a, b int) int {
|
||
return if a > b {
|
||
a
|
||
} else {
|
||
b
|
||
}
|
||
}
|
||
|
||
fn replace_op(s string) string {
|
||
last_char := s[s.len - 1]
|
||
suffix := match last_char {
|
||
`+` {
|
||
'_plus'
|
||
}
|
||
`-` {
|
||
'_minus'
|
||
}
|
||
`*` {
|
||
'_mult'
|
||
}
|
||
`/` {
|
||
'_div'
|
||
}
|
||
`%` {
|
||
'_mod'
|
||
}
|
||
else {
|
||
''
|
||
}
|
||
}
|
||
return s[..s.len - 1] + suffix
|
||
}
|
||
|
||
pub fn join_env_vflags_and_os_args() []string {
|
||
vosargs := os.getenv('VOSARGS')
|
||
if vosargs != '' {
|
||
return non_empty(vosargs.split(' '))
|
||
}
|
||
mut args := []string
|
||
vflags := os.getenv('VFLAGS')
|
||
if vflags != '' {
|
||
args << os.args[0]
|
||
args << vflags.split(' ')
|
||
if os.args.len > 1 {
|
||
args << os.args[1..]
|
||
}
|
||
return non_empty(args)
|
||
}
|
||
return non_empty(os.args)
|
||
}
|
||
|
||
fn non_empty(arg []string) []string {
|
||
return arg.filter(it != '')
|
||
}
|