v / cmd / tools / vup.v
271 lines · 247 sloc · 8.09 KB · f4f715fcf174daac82a4659203139f8863ae6f2b
Raw
1module main
2
3import os
4import v.util.version
5import v.util.recompilation
6
7const vexe = os.real_path(os.getenv_opt('VEXE') or { @VEXE })
8
9const vroot = os.dir(vexe)
10const v_upstream_url = 'https://github.com/vlang/v'
11const v_upstream_branch = 'master'
12
13struct App {
14 is_verbose bool
15 is_prod bool
16 vexe string
17 vroot string
18
19 skip_v_self bool // do not run `v self`, effectively enforcing the running of `make` or `makev.bat`
20 skip_current bool // skip the current hash check, enabling easier testing on the same commit, without using docker etc
21}
22
23const args = arguments()
24
25fn new_app() App {
26 return App{
27 is_verbose: '-v' in args
28 is_prod: '-prod' in args
29 vexe: vexe
30 vroot: vroot
31 skip_v_self: '-skip_v_self' in args
32 skip_current: '-skip_current' in args
33 }
34}
35
36fn main() {
37 app := new_app()
38 recompilation.must_be_enabled(app.vroot, 'Please install V from source, to use `v up` .')
39 os.chdir(app.vroot)!
40 println('Updating V...')
41 app.update_from_master()
42 hash_when_vup_was_compiled := @VCURRENTHASH
43 current_hash_from_filesystem := version.githash(vroot) or { hash_when_vup_was_compiled }
44 if !app.skip_current && hash_when_vup_was_compiled == current_hash_from_filesystem {
45 println('V is already updated.')
46 if !os.exists(app.current_vexe_path()) {
47 eprintln('`${app.vexe}` is missing, trying `${get_make_cmd_name()}` to restore it...')
48 if !app.make('') {
49 app.show_current_v_version()
50 eprintln('Recompiling V *failed*.')
51 eprintln('Try running `${get_make_cmd_name()}` .')
52 exit(1)
53 }
54 }
55 app.show_current_v_version()
56 return
57 }
58 if os.user_os() == 'windows' {
59 app.backup('cmd/tools/vup.exe')
60 }
61 if !app.recompile_v() {
62 app.show_current_v_version()
63 eprintln('Recompiling V *failed*.')
64 eprintln('Try running `${get_make_cmd_name()}` .')
65 exit(1)
66 }
67 app.recompile_vup()
68 app.show_current_v_version()
69}
70
71fn (app App) vprintln(s string) {
72 if app.is_verbose {
73 println(s)
74 }
75}
76
77fn (app App) update_from_master() {
78 app.vprintln('> updating from master ...')
79 if !os.exists('.git') {
80 // initialize the folder, as if it had been cloned:
81 app.git_command('git init')
82 app.git_command('git remote add origin ${v_upstream_url}')
83 app.git_command('git fetch')
84 app.git_command('git remote set-head origin ${v_upstream_branch}')
85 app.git_command('git reset --hard origin/${v_upstream_branch}')
86 // Note 1: patterns starting with /, will match only against the root;
87 // `--exclude v` will match also vlib/v/ in addition to ./v; `--exclude /v` will only match ./v
88 // Note 2: patterns ending with / are treated as folders.
89 app.git_command('git clean -xfd --exclude /thirdparty/tcc/ --exclude /v --exclude /v.exe --exclude /v_old --exclude /v_old.exe --exclude /${app.current_vexe_name()} --exclude /${app.current_vbackup_name()} --exclude /.bin/ --exclude /cmd/tools/vup --exclude /cmd/tools/vup.exe')
90 } else {
91 // Rebase avoids merge failures when the local checkout has unrelated history.
92 app.git_command(v_upstream_pull_command())
93 }
94}
95
96fn v_upstream_pull_command() string {
97 return 'git pull --rebase ${v_upstream_url} ${v_upstream_branch}'
98}
99
100fn (app App) recompile_v() bool {
101 // Note: app.vexe is more reliable than just v (which may be a symlink)
102 vexe_path := app.current_vexe_path()
103 if !os.exists(vexe_path) {
104 println('> `${app.vexe}` is missing, running `make`...')
105 return app.make('')
106 }
107 opts := if app.is_prod { '-prod' } else { '' }
108 vself := '${os.quoted_path(vexe_path)} ${opts} self'
109 if app.skip_v_self {
110 return app.make(vself)
111 }
112
113 self_result := os.execute(vself)
114 if self_result.exit_code == 0 {
115 println(self_result.output.trim_space())
116 println('> Done recompiling.')
117 return true
118 } else {
119 println('> `${vself}` failed, running `make`...')
120 app.vprintln(self_result.output.trim_space())
121 }
122 return app.make(vself)
123}
124
125fn (app App) recompile_vup() bool {
126 eprintln('> Recompiling vup.v ...')
127 vexe_path := app.current_vexe_path()
128 if !os.exists(vexe_path) {
129 eprintln('> Skipping recompiling vup.v, `${vexe_path}` is missing.')
130 return false
131 }
132 vup_result := os.execute('${os.quoted_path(vexe_path)} -g cmd/tools/vup.v')
133 if vup_result.exit_code != 0 {
134 eprintln('> Failed recompiling vup.v .')
135 eprintln(vup_result.output)
136 return false
137 }
138 return true
139}
140
141fn (app App) make(_vself string) bool {
142 println('> running make ...')
143 make := get_make_cmd_name()
144 make_result := os.execute(make)
145 if make_result.exit_code != 0 {
146 eprintln('> ${make} failed:')
147 eprintln('> make output:')
148 eprintln(make_result.output)
149 return false
150 }
151 app.vprintln(make_result.output)
152 println('> done running make.')
153 return true
154}
155
156fn (app App) show_current_v_version() {
157 vexe_path := app.current_vexe_path()
158 if !os.exists(vexe_path) {
159 println('Current V version: unavailable (`${app.vexe}` is missing).')
160 return
161 }
162 vout := os.execute('${os.quoted_path(vexe_path)} version')
163 if vout.exit_code >= 0 {
164 mut vversion := vout.output.trim_space()
165 if vout.exit_code == 0 {
166 latest_v_commit := vversion.split(' ').last().all_after('.')
167 latest_v_commit_time := os.execute('git show -s --format=%ci ${latest_v_commit}')
168 if latest_v_commit_time.exit_code == 0 {
169 vversion += ', timestamp: ' + latest_v_commit_time.output.trim_space()
170 }
171 }
172 println('Current V version: ${vversion}')
173 }
174}
175
176fn (app App) current_vexe_name() string {
177 vexe_name := os.file_name(app.vexe)
178 if vexe_name == '' {
179 return if os.user_os() == 'windows' { 'v.exe' } else { 'v' }
180 }
181 return vexe_name
182}
183
184fn (app App) current_vbackup_name() string {
185 vexe_name := app.current_vexe_name()
186 short_v_name := vexe_name.all_before('.')
187 return if os.user_os() == 'windows' { '${short_v_name}_old.exe' } else { '${short_v_name}_old' }
188}
189
190fn (app App) current_vexe_path() string {
191 if os.exists(app.vexe) {
192 return app.vexe
193 }
194 default_vexe := os.join_path_single(app.vroot, if os.user_os() == 'windows' {
195 'v.exe'
196 } else {
197 'v'
198 })
199 if os.exists(default_vexe) {
200 return default_vexe
201 }
202 configured_vexe := os.join_path_single(app.vroot, app.current_vexe_name())
203 if os.exists(configured_vexe) {
204 return configured_vexe
205 }
206 return app.vexe
207}
208
209fn (app App) backup(file string) {
210 backup_file := '${file}_old.exe'
211 println('> backing up `${file}` to `${backup_file}` ...')
212 if os.exists(backup_file) {
213 os.rm(backup_file) or { eprintln('failed removing ${backup_file}: ${err.msg()}') }
214 }
215 os.mv(file, backup_file) or { eprintln('failed moving ${file}: ${err.msg()}') }
216}
217
218fn (app App) git_command(command string) {
219 println('> git_command: ${command}')
220 git_result := os.execute(command)
221 if git_result.exit_code < 0 {
222 app.install_git()
223 // Try it again with (maybe) git installed
224 os.execute_or_exit(command)
225 }
226 if git_result.exit_code != 0 {
227 eprintln('Failed git command: ${command}')
228 eprintln(git_result.output)
229 exit(1)
230 }
231 app.vprintln(git_result.output)
232}
233
234fn (app App) install_git() {
235 if os.user_os() != 'windows' {
236 // Probably some kind of *nix, usually need to get using a package manager.
237 eprintln("error: Install `git` using your system's package manager")
238 }
239 println('Downloading git 32 bit for Windows, please wait.')
240 // We'll use 32 bit because maybe someone out there is using 32-bit windows
241 res_download :=
242 os.execute('bitsadmin.exe /transfer "vgit" https://github.com/git-for-windows/git/releases/download/v2.30.0.windows.2/Git-2.30.0.2-32-bit.exe "${os.getwd()}/git32.exe"')
243 if res_download.exit_code != 0 {
244 eprintln('Unable to install git automatically: please install git manually')
245 panic(res_download.output)
246 }
247 res_git32 := os.execute(os.quoted_path(os.join_path_single(os.getwd(), 'git32.exe')))
248 if res_git32.exit_code != 0 {
249 eprintln('Unable to install git automatically: please install git manually')
250 panic(res_git32.output)
251 }
252}
253
254fn get_make_cmd_name() string {
255 if os.user_os() == 'windows' {
256 return 'makev.bat'
257 }
258 cmd := 'make'
259 make_sure_cmd_is_available(cmd)
260 cc := os.getenv_opt('CC') or { 'cc' }
261 make_sure_cmd_is_available(cc)
262 return cmd
263}
264
265fn make_sure_cmd_is_available(cmd string) {
266 found_path := os.find_abs_path_of_executable(cmd) or {
267 eprintln('Could not find `${cmd}` in PATH. Please install `${cmd}`, since `v up` needs it.')
268 exit(1)
269 }
270 println('Found `${cmd}` as `${found_path}`.')
271}
272