v/vlib/v/util/util.v

249 lines
6.4 KiB
V
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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 != '')
}