v / cmd / tools
Raw file | 189 loc (181 sloc) | 6.9 KB | Latest commit hash 017ace6ea
1import os
2import flag
3import scripting
4import vgit
5
6const (
7 tool_version = '0.0.4'
8 tool_description = ' Checkout an old V and compile it as it was on specific commit.
9| This tool is useful, when you want to discover when something broke.
10| It is also useful, when you just want to experiment with an older historic V.
11|
12| The VCOMMIT argument can be a git commitish like HEAD or master and so on.
13| When oldv is used with git bisect, you probably want to give HEAD. For example:
14| git bisect start
15| git bisect bad
16| git checkout known_good_commit
17| git bisect good
18| ## Now git will automatically checkout a middle commit between the bad and the good
19| cmd/tools/oldv --bisect --command="run commands in oldv folder, to verify if the commit is good or bad"
20| ## See what the result is, and either do: ...
21| git bisect good
22| ## ... or do:
23| git bisect bad
24| ## Now you just repeat the above steps, each time running oldv with the same command, then mark the result as good or bad,
25| ## until you find the commit, where the problem first occurred.
26| ## When you finish, do not forget to do:
27| git bisect reset'.strip_margin()
28)
29
30struct Context {
31mut:
32 vgo vgit.VGitOptions
33 vgcontext vgit.VGitContext
34 commit_v string = 'master' // the commit from which you want to produce a working v compiler (this may be a commit-ish too)
35 commit_v_hash string // this will be filled from the commit-ish commit_v using rev-list. It IS a commit hash.
36 path_v string // the full path to the v folder inside workdir.
37 path_vc string // the full path to the vc folder inside workdir.
38 cmd_to_run string // the command that you want to run *in* the oldv repo
39 cc string = 'cc' // the C compiler to use for bootstrapping.
40 cleanup bool // should the tool run a cleanup first
41 use_cache bool // use local cached copies for --vrepo and --vcrepo in
42 fresh_tcc bool // do use `make fresh_tcc`
43 is_bisect bool // bisect mode; usage: `cmd/tools/oldv -b -c './v run bug.v'`
44}
45
46fn (mut c Context) compile_oldv_if_needed() {
47 c.vgcontext = vgit.VGitContext{
48 workdir: c.vgo.workdir
49 v_repo_url: c.vgo.v_repo_url
50 vc_repo_url: c.vgo.vc_repo_url
51 cc: c.cc
52 commit_v: c.commit_v
53 path_v: c.path_v
54 path_vc: c.path_vc
55 make_fresh_tcc: c.fresh_tcc
56 }
57 c.vgcontext.compile_oldv_if_needed()
58 c.commit_v_hash = c.vgcontext.commit_v__hash
59 if !os.exists(c.vgcontext.vexepath) && c.cmd_to_run.len > 0 {
60 // Note: 125 is a special code, that git bisect understands as 'skip this commit'.
61 // it is used to inform git bisect that the current commit leads to a build failure.
62 exit(125)
63 }
64}
65
66const cache_oldv_folder = os.join_path(os.cache_dir(), 'oldv')
67
68const cache_oldv_folder_v = os.join_path(cache_oldv_folder, 'v')
69
70const cache_oldv_folder_vc = os.join_path(cache_oldv_folder, 'vc')
71
72fn sync_cache() {
73 scripting.verbose_trace(@FN, 'start')
74 if !os.exists(cache_oldv_folder) {
75 scripting.verbose_trace(@FN, 'creating ${cache_oldv_folder}')
76 scripting.mkdir_all(cache_oldv_folder) or {
77 scripting.verbose_trace(@FN, '## failed.')
78 exit(1)
79 }
80 }
81 scripting.chdir(cache_oldv_folder)
82 for reponame in ['v', 'vc'] {
83 repofolder := os.join_path(cache_oldv_folder, reponame)
84 if !os.exists(repofolder) {
85 scripting.verbose_trace(@FN, 'cloning to ${repofolder}')
86 mut repo_options := ''
87 if reponame == 'vc' {
88 repo_options = '--filter=blob:none'
89 }
90 scripting.exec('git clone ${repo_options} --quiet https://github.com/vlang/${reponame} ${repofolder}') or {
91 scripting.verbose_trace(@FN, '## error during clone: ${err}')
92 exit(1)
93 }
94 }
95 scripting.chdir(repofolder)
96 scripting.exec('git pull --quiet') or {
97 scripting.verbose_trace(@FN, 'pulling to ${repofolder}')
98 scripting.verbose_trace(@FN, '## error during pull: ${err}')
99 exit(1)
100 }
101 }
102 scripting.verbose_trace(@FN, 'done')
103}
104
105fn main() {
106 scripting.used_tools_must_exist(['git'])
107 //
108 // Resetting VEXE here allows for `v run cmd/tools/oldv.v'.
109 // the parent V would have set VEXE, which later will
110 // affect the V's run from the tool itself.
111 os.setenv('VEXE', '', true)
112 //
113 mut context := Context{}
114 context.vgo.workdir = cache_oldv_folder
115 mut fp := flag.new_flag_parser(os.args)
116 fp.application(os.file_name(os.executable()))
117 fp.version(tool_version)
118 fp.description(tool_description)
119 fp.arguments_description('VCOMMIT')
120 fp.skip_executable()
121 context.use_cache = fp.bool('cache', `u`, true, 'Use a cache of local repositories for --vrepo and --vcrepo in \$HOME/.cache/oldv/')
122 if context.use_cache {
123 context.vgo.v_repo_url = cache_oldv_folder_v
124 context.vgo.vc_repo_url = cache_oldv_folder_vc
125 } else {
126 context.vgo.v_repo_url = 'https://github.com/vlang/v'
127 context.vgo.vc_repo_url = 'https://github.com/vlang/vc'
128 }
129 should_sync := fp.bool('cache-sync', `s`, false, 'Update the local cache')
130 context.is_bisect = fp.bool('bisect', `b`, false, 'Bisect mode. Use the current commit in the repo where oldv is.')
131 if !should_sync && !context.is_bisect {
132 fp.limit_free_args(1, 1)!
133 }
134 ////
135 context.cleanup = fp.bool('clean', 0, false, 'Clean before running (slower).')
136 context.fresh_tcc = fp.bool('fresh_tcc', 0, true, 'Do `make fresh_tcc` when preparing a V compiler.')
137 context.cmd_to_run = fp.string('command', `c`, '', 'Command to run in the old V repo.\n')
138 commits := vgit.add_common_tool_options(mut context.vgo, mut fp)
139 if should_sync {
140 sync_cache()
141 exit(0)
142 }
143 if context.use_cache {
144 if !os.is_dir(cache_oldv_folder_v) || !os.is_dir(cache_oldv_folder_vc) {
145 sync_cache()
146 }
147 }
148 if commits.len > 0 {
149 context.commit_v = commits[0]
150 if context.is_bisect {
151 eprintln('In bisect mode, you should not pass any commits, since oldv will use the current one.')
152 exit(2)
153 }
154 } else {
155 context.commit_v = scripting.run('git rev-list -n1 HEAD')
156 }
157 scripting.cprintln('################# context.commit_v: ${context.commit_v} #####################')
158 context.path_v = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_v)
159 context.path_vc = vgit.normalized_workpath_for_commit(context.vgo.workdir, 'vc')
160 if !os.is_dir(context.vgo.workdir) {
161 eprintln('Work folder: ${context.vgo.workdir} , does not exist.')
162 exit(2)
163 }
164 ecc := os.getenv('CC')
165 if ecc != '' {
166 context.cc = ecc
167 }
168 if context.cleanup {
169 scripting.rmrf(context.path_v)
170 scripting.rmrf(context.path_vc)
171 }
172 context.compile_oldv_if_needed()
173 scripting.chdir(context.path_v)
174 shorter_hash := context.commit_v_hash[0..10]
175 scripting.cprintln('# v commit hash: ${shorter_hash} | folder: ${context.path_v}')
176 if context.cmd_to_run.len > 0 {
177 scripting.cprintln_strong('# command: ${context.cmd_to_run:-34s}')
178 cmdres := os.execute_or_exit(context.cmd_to_run)
179 if cmdres.exit_code != 0 {
180 scripting.cprintln_strong('# exit code: ${cmdres.exit_code:-4d}')
181 }
182 scripting.cprint_strong('# result: ')
183 print(cmdres.output)
184 if !cmdres.output.ends_with('\n') {
185 println('')
186 }
187 exit(cmdres.exit_code)
188 }
189}