mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
move v.v to cmd/v
This commit is contained in:
100
cmd/tools/modules/scripting/scripting.v
Normal file
100
cmd/tools/modules/scripting/scripting.v
Normal file
@ -0,0 +1,100 @@
|
||||
module scripting
|
||||
|
||||
import os
|
||||
|
||||
pub fn set_verbose(on bool) {
|
||||
// setting a global here would be the obvious solution,
|
||||
// but V does not have globals normally.
|
||||
if on {
|
||||
os.setenv('VERBOSE', '1', true)
|
||||
}
|
||||
else {
|
||||
os.unsetenv('VERBOSE')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbose_trace(label string, message string) {
|
||||
if os.getenv('VERBOSE').len > 0 {
|
||||
slabel := 'scripting.${label}'
|
||||
println('# ${slabel:-25s} : $message')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbose_trace_exec_result(x os.Result) {
|
||||
if os.getenv('VERBOSE').len > 0 {
|
||||
println('# cmd.exit_code : ${x.exit_code.str():-4s} cmd.output:')
|
||||
println('# ----------------------------------- #')
|
||||
mut lnum := 1
|
||||
lines := x.output.split_into_lines()
|
||||
for line in lines {
|
||||
println('# ${lnum:3d}: $line')
|
||||
lnum++
|
||||
}
|
||||
println('# ----------------------------------- #')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chdir(path string) {
|
||||
verbose_trace(@FN, 'cd $path')
|
||||
os.chdir(path)
|
||||
}
|
||||
|
||||
pub fn rmrf(path string) {
|
||||
verbose_trace(@FN, 'rm -rf $path')
|
||||
if os.exists(path) {
|
||||
if os.is_dir(path) {
|
||||
os.rmdir_recursive(path)
|
||||
}else{
|
||||
os.rm(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(cmd string) string {
|
||||
verbose_trace(@FN, cmd)
|
||||
x := os.exec(cmd) or {
|
||||
verbose_trace(@FN, '## failed.')
|
||||
return ''
|
||||
}
|
||||
verbose_trace_exec_result(x)
|
||||
if x.exit_code == 0 {
|
||||
return x.output
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
pub fn exit_0_status(cmd string) bool {
|
||||
verbose_trace(@FN, cmd)
|
||||
x := os.exec(cmd) or {
|
||||
verbose_trace(@FN, '## failed.')
|
||||
return false
|
||||
}
|
||||
verbose_trace_exec_result(x)
|
||||
if x.exit_code == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn tool_must_exist (toolcmd string) {
|
||||
verbose_trace(@FN, toolcmd)
|
||||
if exit_0_status('type $toolcmd') {
|
||||
return
|
||||
}
|
||||
eprintln('Missing tool: $toolcmd')
|
||||
eprintln('Please try again after you install it.')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
pub fn used_tools_must_exist(tools []string) {
|
||||
for t in tools {
|
||||
tool_must_exist(t)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_sizes_of_files(files []string) {
|
||||
for f in files {
|
||||
size := os.file_size(f)
|
||||
println('${size:10d} $f')
|
||||
}
|
||||
}
|
272
cmd/tools/modules/testing/common.v
Normal file
272
cmd/tools/modules/testing/common.v
Normal file
@ -0,0 +1,272 @@
|
||||
module testing
|
||||
|
||||
import (
|
||||
os
|
||||
term
|
||||
benchmark
|
||||
filepath
|
||||
runtime
|
||||
sync
|
||||
)
|
||||
|
||||
pub struct TestSession {
|
||||
pub mut:
|
||||
files []string
|
||||
vexe string
|
||||
vargs string
|
||||
failed bool
|
||||
benchmark benchmark.Benchmark
|
||||
|
||||
ntask int // writing to this should be locked by mu.
|
||||
ntask_mtx &sync.Mutex
|
||||
waitgroup &sync.WaitGroup
|
||||
show_ok_tests bool
|
||||
}
|
||||
|
||||
pub fn new_test_session(vargs string) TestSession {
|
||||
return TestSession{
|
||||
vexe: vexe_path()
|
||||
vargs: vargs
|
||||
|
||||
ntask: 0
|
||||
ntask_mtx: sync.new_mutex()
|
||||
waitgroup: sync.new_waitgroup()
|
||||
|
||||
show_ok_tests: !vargs.contains('-silent')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vexe_path() string {
|
||||
// NB: tools extracted from v require that the VEXE
|
||||
// environment variable contains the path to the v executable location.
|
||||
// They are usually launched by cmd/v/simple_tool.v,
|
||||
// launch_tool/1 , which provides it.
|
||||
return os.getenv('VEXE')
|
||||
}
|
||||
|
||||
pub fn (ts mut TestSession) init() {
|
||||
ts.benchmark = benchmark.new_benchmark()
|
||||
}
|
||||
|
||||
pub fn (ts mut TestSession) test() {
|
||||
ts.init()
|
||||
mut remaining_files := []string
|
||||
for dot_relative_file in ts.files {
|
||||
relative_file := dot_relative_file.replace('./', '')
|
||||
file := os.realpath(relative_file)
|
||||
$if windows {
|
||||
if file.contains('sqlite') || file.contains('httpbin') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
$if !macos {
|
||||
if file.contains('customer') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
$if msvc {
|
||||
if file.contains('asm') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
$if tinyc {
|
||||
if file.contains('asm') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
remaining_files << dot_relative_file
|
||||
}
|
||||
|
||||
ts.files = remaining_files
|
||||
ts.benchmark.set_total_expected_steps(remaining_files.len)
|
||||
|
||||
mut njobs := runtime.nr_jobs()
|
||||
$if msvc {
|
||||
// NB: MSVC can not be launched in parallel, without giving it
|
||||
// the option /FS because it uses a shared PDB file, which should
|
||||
// be locked, but that makes writing slower...
|
||||
// See: https://docs.microsoft.com/en-us/cpp/build/reference/fs-force-synchronous-pdb-writes?view=vs-2019
|
||||
// Instead, just run tests on 1 core for now.
|
||||
njobs = 1
|
||||
}
|
||||
ts.waitgroup.add( njobs )
|
||||
for i:=0; i < njobs; i++ {
|
||||
go process_in_thread(ts)
|
||||
}
|
||||
ts.waitgroup.wait()
|
||||
ts.benchmark.stop()
|
||||
eprintln(term.h_divider('-'))
|
||||
}
|
||||
|
||||
|
||||
fn process_in_thread(ts mut TestSession){
|
||||
ts.process_files()
|
||||
ts.waitgroup.done()
|
||||
}
|
||||
|
||||
fn (ts mut TestSession) process_files() {
|
||||
tmpd := os.tmpdir()
|
||||
show_stats := '-stats' in ts.vargs.split(' ')
|
||||
|
||||
mut tls_bench := benchmark.new_benchmark() // tls_bench is used to format the step messages/timings
|
||||
tls_bench.set_total_expected_steps( ts.benchmark.nexpected_steps )
|
||||
for {
|
||||
|
||||
ts.ntask_mtx.lock()
|
||||
ts.ntask++
|
||||
idx := ts.ntask-1
|
||||
ts.ntask_mtx.unlock()
|
||||
|
||||
if idx >= ts.files.len { break }
|
||||
tls_bench.cstep = idx
|
||||
|
||||
dot_relative_file := ts.files[ idx ]
|
||||
relative_file := dot_relative_file.replace('./', '')
|
||||
file := os.realpath(relative_file)
|
||||
// Ensure that the generated binaries will be stored in the temporary folder.
|
||||
// Remove them after a test passes/fails.
|
||||
fname := filepath.filename(file)
|
||||
generated_binary_fname := if os.user_os() == 'windows' { fname.replace('.v', '.exe') } else { fname.replace('.v', '') }
|
||||
generated_binary_fpath := filepath.join(tmpd,generated_binary_fname)
|
||||
if os.exists(generated_binary_fpath) {
|
||||
os.rm(generated_binary_fpath)
|
||||
}
|
||||
mut cmd_options := [ts.vargs]
|
||||
if !ts.vargs.contains('fmt') {
|
||||
cmd_options << ' -o "$generated_binary_fpath"'
|
||||
}
|
||||
cmd := '"${ts.vexe}" ' + cmd_options.join(' ') + ' "${file}"'
|
||||
// eprintln('>>> v cmd: $cmd')
|
||||
ts.benchmark.step()
|
||||
tls_bench.step()
|
||||
if show_stats {
|
||||
eprintln(term.h_divider('-'))
|
||||
status := os.system(cmd)
|
||||
if status == 0 {
|
||||
ts.benchmark.ok()
|
||||
tls_bench.ok()
|
||||
}
|
||||
else {
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
continue
|
||||
}
|
||||
}
|
||||
else {
|
||||
r := os.exec(cmd) or {
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
eprintln(tls_bench.step_message_fail(relative_file))
|
||||
continue
|
||||
}
|
||||
if r.exit_code != 0 {
|
||||
ts.failed = true
|
||||
ts.benchmark.fail()
|
||||
tls_bench.fail()
|
||||
eprintln(tls_bench.step_message_fail('${relative_file}\n`$file`\n (\n$r.output\n)'))
|
||||
}
|
||||
else {
|
||||
ts.benchmark.ok()
|
||||
tls_bench.ok()
|
||||
if ts.show_ok_tests {
|
||||
eprintln(tls_bench.step_message_ok(relative_file))
|
||||
}
|
||||
}
|
||||
}
|
||||
if os.exists(generated_binary_fpath) {
|
||||
os.rm(generated_binary_fpath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vlib_should_be_present(parent_dir string) {
|
||||
vlib_dir := filepath.join(parent_dir,'vlib')
|
||||
if !os.is_dir(vlib_dir) {
|
||||
eprintln('$vlib_dir is missing, it must be next to the V executable')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn v_build_failing(zargs string, folder string) bool {
|
||||
main_label := 'Building $folder ...'
|
||||
finish_label := 'building $folder'
|
||||
vexe := vexe_path()
|
||||
parent_dir := filepath.dir(vexe)
|
||||
vlib_should_be_present(parent_dir)
|
||||
vargs := zargs.replace(vexe, '')
|
||||
eheader(main_label)
|
||||
eprintln('v compiler args: "$vargs"')
|
||||
mut session := new_test_session(vargs)
|
||||
files := os.walk_ext(filepath.join(parent_dir,folder), '.v')
|
||||
mut mains := files.filter(!it.contains('modules') && !it.contains('preludes'))
|
||||
$if windows {
|
||||
// skip pico example on windows
|
||||
// there was a bug using filter here
|
||||
mut mains_filtered := []string
|
||||
for file in mains {
|
||||
if !file.ends_with('examples\\pico\\pico.v') {
|
||||
mains_filtered << file
|
||||
}
|
||||
}
|
||||
mains = mains_filtered
|
||||
}
|
||||
session.files << mains
|
||||
session.test()
|
||||
eprintln(session.benchmark.total_message(finish_label))
|
||||
return session.failed
|
||||
}
|
||||
|
||||
pub fn build_v_cmd_failed(cmd string) bool {
|
||||
res := os.exec(cmd) or {
|
||||
return true
|
||||
}
|
||||
if res.exit_code != 0 {
|
||||
eprintln('')
|
||||
eprintln(res.output)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn building_any_v_binaries_failed() bool {
|
||||
eheader('Building V binaries...')
|
||||
eprintln('VFLAGS is: "' + os.getenv('VFLAGS') + '"')
|
||||
vexe := testing.vexe_path()
|
||||
parent_dir := filepath.dir(vexe)
|
||||
testing.vlib_should_be_present(parent_dir)
|
||||
os.chdir(parent_dir)
|
||||
mut failed := false
|
||||
v_build_commands := ['$vexe -o v_g -g cmd/v',
|
||||
'$vexe -o v_prod_g -prod -g cmd/v',
|
||||
'$vexe -o v_cg -cg cmd/v',
|
||||
'$vexe -o v_prod_cg -prod -cg cmd/v',
|
||||
'$vexe -o v_prod -prod cmd/v',
|
||||
]
|
||||
mut bmark := benchmark.new_benchmark()
|
||||
for cmd in v_build_commands {
|
||||
bmark.step()
|
||||
if build_v_cmd_failed(cmd) {
|
||||
bmark.fail()
|
||||
failed = true
|
||||
eprintln(bmark.step_message_fail('command: ${cmd} . See details above ^^^^^^^'))
|
||||
eprintln('')
|
||||
continue
|
||||
}
|
||||
bmark.ok()
|
||||
eprintln(bmark.step_message_ok('command: ${cmd}'))
|
||||
}
|
||||
bmark.stop()
|
||||
eprintln(term.h_divider('-'))
|
||||
eprintln(bmark.total_message('building v binaries'))
|
||||
return failed
|
||||
}
|
||||
|
||||
pub fn eheader(msg string) {
|
||||
eprintln(term.header(msg,'-'))
|
||||
}
|
||||
|
||||
pub fn header(msg string) {
|
||||
println(term.header(msg,'-'))
|
||||
}
|
178
cmd/tools/modules/vgit/vgit.v
Normal file
178
cmd/tools/modules/vgit/vgit.v
Normal file
@ -0,0 +1,178 @@
|
||||
module vgit
|
||||
|
||||
import os
|
||||
import flag
|
||||
import filepath
|
||||
import scripting
|
||||
|
||||
const (
|
||||
remote_v_repo_url = 'https://github.com/vlang/v'
|
||||
remote_vc_repo_url = 'https://github.com/vlang/vc'
|
||||
)
|
||||
|
||||
pub fn check_v_commit_timestamp_before_self_rebuilding(v_timestamp int) {
|
||||
if v_timestamp >= 1561805697 {
|
||||
return
|
||||
}
|
||||
eprintln('##################################################################')
|
||||
eprintln('# WARNING: v self rebuilding, before 5b7a1e8 (2019-06-29 12:21) #')
|
||||
eprintln('# required the v executable to be built *inside* #')
|
||||
eprintln('# the toplevel compiler/ folder. #')
|
||||
eprintln('# #')
|
||||
eprintln('# That is not supported by this tool. #')
|
||||
eprintln('# You will have to build it manually there. #')
|
||||
eprintln('##################################################################')
|
||||
}
|
||||
|
||||
pub fn validate_commit_exists(commit string) {
|
||||
if commit.len == 0 {
|
||||
return
|
||||
}
|
||||
cmd := "git cat-file -t \'$commit\' "
|
||||
if !scripting.exit_0_status(cmd) {
|
||||
eprintln('Commit: "$commit" does not exist in the current repository.')
|
||||
exit(3)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_to_timestamp_and_commit(line string) (int,string) {
|
||||
parts := line.split(' ')
|
||||
return parts[0].int(),parts[1]
|
||||
}
|
||||
|
||||
pub fn normalized_workpath_for_commit(workdir string, commit string) string {
|
||||
nc := 'v_at_' + commit.replace('^', '_').replace('-', '_').replace('/', '_')
|
||||
return os.realpath(workdir + os.path_separator + nc)
|
||||
}
|
||||
|
||||
pub fn prepare_vc_source(vcdir string, cdir string, commit string) (string,string) {
|
||||
scripting.chdir(cdir)
|
||||
// Building a historic v with the latest vc is not always possible ...
|
||||
// It is more likely, that the vc *at the time of the v commit*,
|
||||
// or slightly before that time will be able to build the historic v:
|
||||
vline := scripting.run('git rev-list -n1 --timestamp "$commit" ')
|
||||
v_timestamp,v_commithash := vgit.line_to_timestamp_and_commit(vline)
|
||||
vgit.check_v_commit_timestamp_before_self_rebuilding(v_timestamp)
|
||||
scripting.chdir(vcdir)
|
||||
scripting.run('git checkout master')
|
||||
vcbefore := scripting.run('git rev-list HEAD -n1 --timestamp --before=$v_timestamp ')
|
||||
_,vccommit_before := vgit.line_to_timestamp_and_commit(vcbefore)
|
||||
scripting.run('git checkout "$vccommit_before" ')
|
||||
scripting.run('wc *.c')
|
||||
scripting.chdir(cdir)
|
||||
return v_commithash,vccommit_before
|
||||
}
|
||||
|
||||
pub fn clone_or_pull( remote_git_url string, local_worktree_path string ) {
|
||||
// NB: after clone_or_pull, the current repo branch is === HEAD === master
|
||||
if os.is_dir( local_worktree_path ) && os.is_dir(filepath.join(local_worktree_path,'.git')) {
|
||||
// Already existing ... Just pulling in this case is faster usually.
|
||||
scripting.run('git -C "$local_worktree_path" checkout --quiet master')
|
||||
scripting.run('git -C "$local_worktree_path" pull --quiet ')
|
||||
} else {
|
||||
// Clone a fresh
|
||||
scripting.run('git clone --quiet "$remote_git_url" "$local_worktree_path" ')
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
pub struct VGitContext {
|
||||
pub:
|
||||
cc string = 'cc' // what compiler to use
|
||||
workdir string = '/tmp' // the base working folder
|
||||
commit_v string = 'master' // the commit-ish that needs to be prepared
|
||||
path_v string // where is the local working copy v repo
|
||||
path_vc string // where is the local working copy vc repo
|
||||
v_repo_url string // the remote v repo URL
|
||||
vc_repo_url string // the remote vc repo URL
|
||||
pub mut:
|
||||
// these will be filled by vgitcontext.compile_oldv_if_needed()
|
||||
commit_v__hash string // the git commit of the v repo that should be prepared
|
||||
commit_vc_hash string // the git commit of the vc repo, corresponding to commit_v__hash
|
||||
vexename string // v or v.exe
|
||||
vexepath string // the full absolute path to the prepared v/v.exe
|
||||
vvlocation string // v.v or compiler/ or cmd/v, depending on v version
|
||||
}
|
||||
|
||||
pub fn (vgit_context mut VGitContext) compile_oldv_if_needed() {
|
||||
vgit_context.vexename = if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
|
||||
vgit_context.vexepath = os.realpath( filepath.join(vgit_context.path_v, vgit_context.vexename) )
|
||||
mut command_for_building_v_from_c_source := ''
|
||||
mut command_for_selfbuilding := ''
|
||||
if 'windows' == os.user_os() {
|
||||
command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" '
|
||||
command_for_selfbuilding = './cv.exe -o $vgit_context.vexename {SOURCE}'
|
||||
}
|
||||
else {
|
||||
command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -w -o cv "$vgit_context.path_vc/v.c" -lm'
|
||||
command_for_selfbuilding = './cv -o $vgit_context.vexename {SOURCE}'
|
||||
}
|
||||
scripting.chdir(vgit_context.workdir)
|
||||
clone_or_pull( vgit_context.v_repo_url, vgit_context.path_v )
|
||||
clone_or_pull( vgit_context.vc_repo_url, vgit_context.path_vc )
|
||||
|
||||
scripting.chdir(vgit_context.path_v)
|
||||
scripting.run('git checkout $vgit_context.commit_v')
|
||||
v_commithash,vccommit_before := vgit.prepare_vc_source(vgit_context.path_vc, vgit_context.path_v, vgit_context.commit_v)
|
||||
vgit_context.commit_v__hash = v_commithash
|
||||
vgit_context.commit_vc_hash = vccommit_before
|
||||
if os.exists('cmd/v') {
|
||||
vgit_context.vvlocation = 'cmd/v'
|
||||
} else {
|
||||
vgit_context.vvlocation = if os.exists('v.v') { 'v.v' } else { 'compiler' }
|
||||
}
|
||||
if os.is_dir(vgit_context.path_v) && os.exists(vgit_context.vexepath) {
|
||||
// already compiled, so no need to compile v again
|
||||
return
|
||||
}
|
||||
// Recompilation is needed. Just to be sure, clean up everything first.
|
||||
scripting.run('git clean -xf')
|
||||
scripting.run(command_for_building_v_from_c_source)
|
||||
build_cmd := command_for_selfbuilding.replace('{SOURCE}', vgit_context.vvlocation)
|
||||
scripting.run(build_cmd)
|
||||
|
||||
// At this point, there exists a file vgit_context.vexepath
|
||||
// which should be a valid working V executable.
|
||||
}
|
||||
|
||||
pub fn add_common_tool_options<T>(context mut T, fp mut flag.FlagParser) []string {
|
||||
tdir := os.tmpdir()
|
||||
context.workdir = os.realpath(fp.string_('workdir', `w`, tdir, 'A writable base folder. Default: $tdir'))
|
||||
context.v_repo_url = fp.string('vrepo', vgit.remote_v_repo_url, 'The url of the V repository. You can clone it locally too. See also --vcrepo below.')
|
||||
context.vc_repo_url = fp.string('vcrepo', vgit.remote_vc_repo_url, 'The url of the vc repository. You can clone it
|
||||
${flag.SPACE}beforehand, and then just give the local folder
|
||||
${flag.SPACE}path here. That will eliminate the network ops
|
||||
${flag.SPACE}done by this tool, which is useful, if you want
|
||||
${flag.SPACE}to script it/run it in a restrictive vps/docker.
|
||||
')
|
||||
context.show_help = fp.bool_('help', `h`, false, 'Show this help screen.')
|
||||
context.verbose = fp.bool_('verbose', `v`, false, 'Be more verbose.')
|
||||
|
||||
if (context.show_help) {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if context.verbose {
|
||||
scripting.set_verbose(true)
|
||||
}
|
||||
|
||||
if os.is_dir(context.v_repo_url) {
|
||||
context.v_repo_url = os.realpath( context.v_repo_url )
|
||||
}
|
||||
|
||||
if os.is_dir(context.vc_repo_url) {
|
||||
context.vc_repo_url = os.realpath( context.vc_repo_url )
|
||||
}
|
||||
|
||||
commits := fp.finalize() or {
|
||||
eprintln('Error: ' + err)
|
||||
exit(1)
|
||||
}
|
||||
for commit in commits {
|
||||
vgit.validate_commit_exists(commit)
|
||||
}
|
||||
|
||||
return commits
|
||||
}
|
Reference in New Issue
Block a user