mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
tooling: add tools/oldv , for use with 'git bisect', or for testing with older V
This commit is contained in:
parent
4d1f721558
commit
1bb38a38bb
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,7 +7,7 @@ fns.txt
|
||||
/v.*.c
|
||||
/v.c.out
|
||||
/tools/performance_compare
|
||||
/tools/performance_compare.exe
|
||||
/tools/oldv
|
||||
/tools/vrepl
|
||||
/tools/vtest
|
||||
/tools/vtest-compiler
|
||||
|
72
tools/modules/scripting/scripting.v
Normal file
72
tools/modules/scripting/scripting.v
Normal file
@ -0,0 +1,72 @@
|
||||
module scripting
|
||||
|
||||
import os
|
||||
|
||||
pub fn verbose_trace(label string, message string){
|
||||
if os.getenv('VERBOSE').len > 0 {
|
||||
slabel := 'scripting.${label}'
|
||||
println('# ${slabel:30s} : $message')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbose_trace_exec_result(x os.Result) {
|
||||
if os.getenv('VERBOSE').len > 0 {
|
||||
println('# cmd.exit_code : ${x.exit_code.str()}')
|
||||
println('# 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 run(cmd string) string {
|
||||
verbose_trace(@FN, cmd)
|
||||
x := os.exec(cmd) or { return '' }
|
||||
verbose_trace_exec_result( x )
|
||||
if x.exit_code == 0 { return x.output }
|
||||
return ''
|
||||
}
|
||||
|
||||
pub fn command_exits_with_zero_status(cmd string) bool {
|
||||
verbose_trace(@FN, cmd)
|
||||
x := os.exec(cmd) or { 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 command_exits_with_zero_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 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('##################################################################')
|
||||
}
|
188
tools/oldv.v
Normal file
188
tools/oldv.v
Normal file
@ -0,0 +1,188 @@
|
||||
import (
|
||||
os
|
||||
flag
|
||||
filepath
|
||||
scripting
|
||||
)
|
||||
|
||||
const (
|
||||
tool_version = '0.0.2'
|
||||
tool_description = 'Checkout an old V and compile it. Useful when you want to discover when something broke.'
|
||||
remote_repo_url_v = 'https://github.com/vlang/v'
|
||||
remote_repo_url_vc = 'https://github.com/vlang/vc'
|
||||
)
|
||||
|
||||
struct Context {
|
||||
mut:
|
||||
repo_url_v string // the url of the V repository. It can be a local folder path, if you want to eliminate network operations...
|
||||
repo_url_vc string // the url of the vc repository. It can be a local folder path, if you want to eliminate network operations...
|
||||
workdir string // the working folder (typically /tmp), where the tool will write
|
||||
commit_v string = 'master' // the commit from which you want to produce a working v compiler (this may be a commit-ish too)
|
||||
commit_vc string = 'master' // this will be derived from commit_v
|
||||
commit_v_hash string // this will be filled from the commit-ish commit_v using rev-list. It IS a commit hash.
|
||||
path_v string // the full path to the v folder inside workdir.
|
||||
path_vc string // the full path to the vc folder inside workdir.
|
||||
cmd_to_run string // the command that you want to run *in* the oldv repo
|
||||
cc string = 'cc' // the C compiler to use for bootstrapping.
|
||||
cleanup bool // should the tool run a cleanup first
|
||||
verbose bool // should the tool be much more verbose
|
||||
}
|
||||
|
||||
fn (c mut Context) compile_oldv_if_needed() {
|
||||
vexename := if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
|
||||
vexepath := filepath.join( c.path_v, vexename )
|
||||
|
||||
mut command_for_building_v_from_c_source := ''
|
||||
mut commands_for_selfbuilding := []string
|
||||
if 'windows' == os.user_os() {
|
||||
command_for_building_v_from_c_source = '$c.cc -w -o cv.exe "$c.path_vc/v_win.c" '
|
||||
commands_for_selfbuilding << './cv.exe -o v2.exe {SOURCE}'
|
||||
commands_for_selfbuilding << './v2.exe -o $vexename {SOURCE}'
|
||||
}else{
|
||||
command_for_building_v_from_c_source = '$c.cc -w -o cv "$c.path_vc/v.c" -lm'
|
||||
commands_for_selfbuilding << './cv -o $vexename {SOURCE}'
|
||||
}
|
||||
|
||||
scripting.chdir( c.workdir )
|
||||
scripting.run('git clone --quiet "$c.repo_url_v" "$c.path_v" ')
|
||||
scripting.run('git clone --quiet "$c.repo_url_vc" "$c.path_vc" ')
|
||||
|
||||
scripting.chdir( c.path_v )
|
||||
scripting.run('git checkout $c.commit_v')
|
||||
c.prepare_vc_source( c.commit_v )
|
||||
|
||||
if os.is_dir( c.path_v ) && os.exists( vexepath ) { return }
|
||||
|
||||
scripting.run('git clean -f')
|
||||
source_location := if os.exists('v.v') { 'v.v' } else { 'compiler' }
|
||||
scripting.run( command_for_building_v_from_c_source )
|
||||
for cmd in commands_for_selfbuilding {
|
||||
build_cmd := cmd.replace('{SOURCE}', source_location)
|
||||
scripting.run( build_cmd )
|
||||
}
|
||||
|
||||
if !os.exists( vexepath ) && c.cmd_to_run.len > 0 {
|
||||
// NB: 125 is a special code, that git bisect understands as 'skip this commit'.
|
||||
// it is used to inform git bisect that the current commit leads to a build failure.
|
||||
exit( 125 )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn line_to_timestamp_and_commit(line string) (int, string) {
|
||||
parts := line.split(' ')
|
||||
return parts[0].int(), parts[1]
|
||||
}
|
||||
|
||||
fn (c mut Context) prepare_vc_source( commit string ) {
|
||||
scripting.chdir( c.path_v )
|
||||
// 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 := line_to_timestamp_and_commit( vline )
|
||||
c.commit_v_hash = v_commithash
|
||||
scripting.check_v_commit_timestamp_before_self_rebuilding(v_timestamp)
|
||||
scripting.chdir( c.path_vc )
|
||||
scripting.run('git checkout master')
|
||||
vcbefore := scripting.run('git rev-list HEAD -n1 --timestamp --before=$v_timestamp ')
|
||||
_, vccommit_before := line_to_timestamp_and_commit( vcbefore )
|
||||
c.commit_vc = vccommit_before
|
||||
scripting.run('git checkout "$vccommit_before" ')
|
||||
scripting.chdir( c.path_v )
|
||||
}
|
||||
|
||||
fn (c Context) normalized_workpath_for_commit( commit string ) string {
|
||||
nc := 'v_at_' + commit.replace('^','_').replace('-','_').replace('/','_')
|
||||
return os.realpath( c.workdir + os.path_separator + nc )
|
||||
}
|
||||
|
||||
fn validate_commit_exists( commit string ){
|
||||
cmd := 'git cat-file -t ' + "'" + commit + "'"
|
||||
if !scripting.command_exits_with_zero_status(cmd) {
|
||||
eprintln("Commit: '" + commit + "' does not exist in the current repository.")
|
||||
exit(3)
|
||||
}
|
||||
}
|
||||
|
||||
fn main(){
|
||||
scripting.used_tools_must_exist(['git','cc'])
|
||||
mut context := Context{}
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application(os.filename(os.executable()))
|
||||
fp.version( tool_version )
|
||||
fp.description( tool_description )
|
||||
fp.arguments_description('VCOMMIT')
|
||||
fp.skip_executable()
|
||||
|
||||
show_help:=fp.bool('help', false, 'Show this help screen\n')
|
||||
context.cmd_to_run = fp.string('command', '', 'Command to run in the old V repo.\n')
|
||||
context.cleanup = fp.bool('clean', true, 'Clean before running (slower).\n')
|
||||
context.verbose = fp.bool('verbose', false, 'Be more verbose.\n')
|
||||
|
||||
context.workdir = os.realpath( fp.string('work-dir', os.tmpdir(), 'A writable folder, where the comparison will be done.\n') )
|
||||
|
||||
context.repo_url_v = fp.string('v-repo', remote_repo_url_v, 'The url of the V repository. You can clone it locally too.\n')
|
||||
|
||||
context.repo_url_vc = fp.string('vc-repo', remote_repo_url_vc, '' +
|
||||
'The url of the vc repository. You can clone it \n'+
|
||||
flag.SPACE+'beforehand, and then just give the local folder \n'+
|
||||
flag.SPACE+'path here. That will eliminate the network ops \n'+
|
||||
flag.SPACE+'done by this tool, which is useful, if you want \n'+
|
||||
flag.SPACE+'to script it/run it in a restrictive vps/docker.\n')
|
||||
|
||||
if( show_help ){
|
||||
println( fp.usage() )
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if context.verbose {
|
||||
os.setenv('VERBOSE','true',true)
|
||||
}
|
||||
|
||||
commits := fp.finalize() or {
|
||||
eprintln('Error: ' + err)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
if commits.len > 0 {
|
||||
context.commit_v = commits[0]
|
||||
validate_commit_exists( context.commit_v )
|
||||
}else{
|
||||
context.commit_v = scripting.run('git rev-list -n1 HEAD')
|
||||
}
|
||||
println('################# context.commit_v: $context.commit_v #####################')
|
||||
|
||||
context.path_v = context.normalized_workpath_for_commit( context.commit_v )
|
||||
context.path_vc = context.normalized_workpath_for_commit( 'vc' )
|
||||
|
||||
if !os.is_dir( context.workdir ) {
|
||||
msg := 'Work folder: ' + context.workdir + ' , does not exist.'
|
||||
eprintln(msg)
|
||||
exit(2)
|
||||
}
|
||||
|
||||
ecc := os.getenv('CC')
|
||||
if ecc!='' { context.cc = ecc }
|
||||
|
||||
if context.cleanup {
|
||||
scripting.run('rm -rf $context.path_v')
|
||||
scripting.run('rm -rf $context.path_vc')
|
||||
}
|
||||
|
||||
context.compile_oldv_if_needed()
|
||||
|
||||
scripting.chdir( context.path_v )
|
||||
println('# v commit hash: $context.commit_v_hash')
|
||||
println('# checkout folder: $context.path_v')
|
||||
|
||||
if context.cmd_to_run.len > 0 {
|
||||
cmdres := os.exec( context.cmd_to_run ) or { panic(err) }
|
||||
println('# command: $context.cmd_to_run')
|
||||
println('# command exit code: $cmdres.exit_code')
|
||||
println('# command result :')
|
||||
println(cmdres.output)
|
||||
exit( cmdres.exit_code )
|
||||
}
|
||||
|
||||
}
|
@ -1069,3 +1069,7 @@ pub fn tmpdir() string {
|
||||
pub fn chmod(path string, mode int) {
|
||||
C.chmod(path.str, mode)
|
||||
}
|
||||
|
||||
pub const (
|
||||
wd_at_startup = getwd()
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user