1if not modules then modules = { } end modules ['mtx-update'] = {
2 version = 1.002,
3 comment = "companion to mtxrun.lua",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15
16local helpinfo = [[
17<?xml version="1.0"?>
18<application>
19 <metadata>
20 <entry name="name">mtx-update</entry>
21 <entry name="detail">ConTeXt Minimals Updater</entry>
22 <entry name="version">1.03</entry>
23 </metadata>
24 <flags>
25 <category name="basic">
26 <subcategory>
27 <flag name="platform" value="string"><short>platform (windows, linux, linux-64, osx-intel, osx-ppc, linux-ppc)</short></flag>
28 <flag name="server" value="string"><short>repository url (rsync://contextgarden.net)</short></flag>
29 <flag name="module" value="string"><short>repository url (minimals)</short></flag>
30 <flag name="repository" value="string"><short>specify version (current, experimental)</short></flag>
31 <flag name="context" value="string"><short>specify version (current, latest, beta, yyyy.mm.dd)</short></flag>
32 <flag name="rsync" value="string"><short>rsync binary (rsync)</short></flag>
33 <flag name="texroot" value="string"><short>installation directory (not guessed for the moment)</short></flag>
34 <flag name="engine" value="string"><short>tex engine (luatex, pdftex, xetex)</short></flag>
35 <flag name="modules" value="string"><short>extra modules (can be list or 'all')</short></flag>
36 <flag name="fonts" value="string"><short>additional fonts (can be list or 'all')</short></flag>
37 <flag name="goodies" value="string"><short>extra binaries (like scite and texworks)</short></flag>
38 <flag name="force"><short>instead of a dryrun, do the real thing</short></flag>
39 <flag name="update"><short>update minimal tree</short></flag>
40 <flag name="make"><short>also make formats and generate file databases</short></flag>
41 <flag name="keep"><short>don't delete unused or obsolete files</short></flag>
42 <flag name="state"><short>update tree using saved state</short></flag>
43 <flag name="cygwin"><short>adapt drive specs to cygwin</short></flag>
44 <flag name="mingw"><short>assume mingw binaries being used</short></flag>
45 <flag name="silent"><short>less (or no) logging</short></flag>
46 </subcategory>
47 </category>
48 </flags>
49</application>
50]]
51
52local application = logs.application {
53 name = "mtx-update",
54 banner = "ConTeXt Minimals Updater 1.03",
55 helpinfo = helpinfo,
56}
57
58local report = application.report
59
60local format, concat, gmatch, gsub, find = string.format, table.concat, string.gmatch, string.gsub, string.find
61
62scripts = scripts or { }
63scripts.update = scripts.update or { }
64local update = scripts.update
65
66minimals = minimals or { }
67minimals.config = minimals.config or { }
68
69
70
71
72os.setenv("CYGWIN","nontsec")
73
74update.texformats = {
75 "cont-en",
76 "cont-nl",
77 "cont-cz",
78 "cont-de",
79 "cont-fa",
80 "cont-it",
81 "cont-ro",
82 "cont-uk",
83 "cont-pe",
84
85 "mptopdf",
86 "plain"
87}
88
89
90
91
92
93
94
95
96update.repositories = {
97 "current",
98 "experimental"
99}
100
101
102
103update.base = {
104 { "base/tex/", "texmf" },
105 { "base/metapost/", "texmf" },
106 { "fonts/common/", "texmf" },
107 { "fonts/other/", "texmf" },
108 { "context/<version>/", "texmf-context" },
109 { "misc/setuptex/", "." },
110 { "misc/web2c", "texmf" },
111 { "bin/common/<platform>/", "texmf-<platform>" },
112 { "bin/context/<platform>/", "texmf-<platform>" },
113 { "bin/metapost/<platform>/", "texmf-<platform>" },
114 { "bin/man/", "texmf-<platform>" },
115}
116
117
118
119
120update.defaultengine = "luatex"
121update.rsyncvariant = "cygwin"
122
123update.engines = {
124 ["luatex"] = {
125 { "fonts/new/", "texmf" },
126 { "bin/luatex/<platform>/", "texmf-<platform>" },
127
128 },
129 ["xetex"] = {
130 { "base/xetex/", "texmf" },
131 { "fonts/new/", "texmf" },
132 { "bin/luatex/<platform>/", "texmf-<platform>" },
133 { "bin/xetex/<platform>/", "texmf-<platform>" },
134 },
135 ["pdftex"] = {
136 { "fonts/old/", "texmf" },
137 { "bin/luatex/<platform>/", "texmf-<platform>" },
138 { "bin/pdftex/<platform>/", "texmf-<platform>" },
139 },
140 ["all"] = {
141 { "fonts/new/", "texmf" },
142 { "fonts/old/", "texmf" },
143 { "base/xetex/", "texmf" },
144 { "bin/luatex/<platform>/", "texmf-<platform>" },
145
146 { "bin/xetex/<platform>/", "texmf-<platform>" },
147 { "bin/pdftex/<platform>/", "texmf-<platform>" },
148 },
149}
150
151update.goodies = {
152 ["scite"] = {
153 { "bin/<platform>/scite/", "texmf-<platform>" },
154 },
155 ["texworks"] = {
156 { "bin/<platform>/texworks/", "texmf-<platform>" },
157 },
158}
159
160update.platforms = {
161
162
163
164
165
166 ["windows"] = "win64",
167 ["windows-64"] = "win64",
168 ["win64"] = "win64",
169
170
171
172
173
174 ["linux"] = "linux-64",
175 ["linux-64"] = "linux-64",
176 ["linux64"] = "linux-64",
177
178 ["linuxmusl"] = "linuxmusl",
179 ["linuxmusl-64"] = "linuxmusl-64",
180
181
182
183 ["openbsd"] = "openbsd-amd64",
184
185 ["openbsd-amd64"] = "openbsd-amd64",
186
187 ["freebsd"] = "freebsd-amd64",
188
189 ["freebsd-amd64"] = "freebsd-amd64",
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 ["macosx"] = "osx-64",
209 ["osx"] = "osx-64",
210 ["osx-intel"] = "osx-64",
211 ["osx-64"] = "osx-64",
212 ["osx-arm"] = "osx-arm64",
213 ["osx-arm64"] = "osx-arm64",
214
215
216
217
218
219
220 ["unknown"] = "unknown",
221}
222
223local windowsplatform = {
224 ["mswin"] = true,
225 ["win32"] = true,
226 ["win64"] = true,
227}
228
229update.selfscripts = {
230 "mtxrun",
231
232}
233
234
235
236update.modules = {
237}
238
239update.fonts = {
240}
241
242function update.run(str)
243
244
245 os.setenv("engine",nil)
246 if environment.argument("force") then
247 report("run, %s",str)
248 os.execute(str)
249 else
250 report("dry run, %s",str)
251 end
252end
253
254function update.fullpath(path)
255 if file.is_rootbased_path(path) then
256 return path
257 else
258 return lfs.currentdir() .. "/" .. path
259 end
260end
261
262local function drive(d)
263 if update.rsyncvariant == "cygwin" then
264 d = gsub(d,[[([a-zA-Z]):/]], "/cygdrive/%1/")
265 else
266 d = gsub(d,[[([a-zA-Z]):/]], "/%1/")
267 end
268 return d
269end
270
271function update.synchronize()
272
273 report("update, start")
274
275 local texroot = update.fullpath(states.get("paths.root"))
276 local engines = states.get('engines') or { }
277 local platforms = states.get('platforms') or { }
278 local repositories = states.get('repositories')
279 local bin = states.get("rsync.program")
280 local url = states.get("rsync.server")
281 local version = states.get("context.version")
282 local modules = states.get("modules")
283 local fonts = states.get("fonts")
284 local goodies = states.get("goodies")
285 local force = environment.argument("force")
286 local silent = environment.argument("silent") and "--silent" or ""
287 local quiet = silent == "" and "" or "--quiet"
288
289 bin = gsub(bin,"\\","/")
290
291 if not find(url,"::$") then url = url .. "::" end
292 local ok = lfs.attributes(texroot,"mode") == "directory"
293 if not ok and force then
294 dir.mkdirs(texroot)
295 ok = lfs.attributes(texroot,"mode") == "directory"
296 end
297
298 if force then
299 dir.mkdirs(format("%s/%s", texroot, "texmf-cache"))
300 dir.mkdirs(format("%s/%s", texroot, "texmf-local"))
301 dir.mkdirs(format("%s/%s", texroot, "texmf-project"))
302 dir.mkdirs(format("%s/%s", texroot, "texmf-fonts"))
303 dir.mkdirs(format("%s/%s", texroot, "texmf-modules"))
304 end
305
306 if ok or not force then
307
308 local fetched, individual, osplatform = { }, { }, os.platform
309
310
311
312 local function collection_to_list_of_folders(collection, platform)
313 local archives = {}
314 for i=1,#collection do
315 local archive = collection[i][1]
316 archive = gsub(archive,"<platform>",platform)
317 archive = gsub(archive,"<version>",version)
318 archives[#archives+1] = archive
319 end
320 return archives
321 end
322
323
324
325
326
327
328
329 local function list_of_folders_to_rsync_string(list_of_folders)
330 local repository = 'current'
331 local prefix = format("%s/%s/", states.get('rsync.module'), repository)
332
333 return prefix .. concat(list_of_folders, format(" %s", prefix))
334 end
335
336
337
338
339
340
341
342
343 local function get_list_of_files_from_rsync(list_of_folders)
344
345 local temp_file = "rsync.tmp.txt"
346
347 local folders = {}
348 local command = format("%s %s'%s' > %s", bin, url, list_of_folders_to_rsync_string(list_of_folders), temp_file)
349 os.execute(command)
350
351 local data = io.loaddata(temp_file) or ""
352
353 for chmod, s in gmatch(data,"([d%-][rwxst%-]+).-(%S+)[\n\r]") do
354
355 if s ~= '.' and #chmod >= 10 then
356 folders[#folders+1] = s
357 end
358 end
359
360 os.remove(temp_file)
361 return folders
362 end
363
364
365
366 local available_platforms = get_list_of_files_from_rsync({"bin/luatex/"})
367
368 report("available platforms: % t",table.sorted(available_platforms))
369
370 if modules and type(modules) == "table" then
371
372
373
374
375 local available_modules = get_list_of_files_from_rsync({"modules/"})
376 local asked = table.copy(modules)
377 asked.all = nil
378 report("available modules: %s",#available_modules)
379 for i=1,#available_modules do
380 local s = available_modules[i]
381 if modules.all or modules[s] then
382 update.modules[#update.modules+1] = { format("modules/%s/",s), "texmf-modules" }
383 report("+ %s",s)
384 else
385 report(" %s",s)
386 end
387 asked[s] = nil
388 end
389 if next(asked) then
390 report("skipping unknown modules: %s",concat(table.sortedkeys(asked),", "))
391 end
392 end
393
394
395
396 if fonts and type(fonts) == "table" then
397 local available_fonts = get_list_of_files_from_rsync({"fonts/extra/"})
398 local asked = table.copy(fonts)
399 asked.all = nil
400 for i=1,#available_fonts do
401 local s = available_fonts[i]
402 if fonts.all or fonts[s] then
403 update.fonts[#update.fonts+1] = { format("fonts/extra/%s/",s), "texmf" }
404 end
405 asked[s] = nil
406 end
407 if next(asked) then
408 report("skipping unknown fonts: %s",concat(table.sortedkeys(asked),", "))
409 end
410 end
411
412 local function add_collection(collection,platform)
413 if collection and platform then
414 platform = update.platforms[platform]
415 if platform then
416 for i=1,#collection do
417 local c = collection[i]
418 local archive = gsub(c[1],"<platform>",platform)
419 local destination = format("%s/%s", texroot, gsub(c[2],"<platform>", platform))
420 destination = gsub(destination,"\\","/")
421 archive = gsub(archive,"<version>",version)
422 if osplatform == "windows" or osplatform == "mswin" or osplatform == "win64" then
423 destination = drive(destination)
424 end
425 individual[#individual+1] = { archive, destination }
426 end
427 end
428 end
429 end
430
431 for platform in table.sortedhash(platforms) do
432 add_collection(update.base,platform)
433 end
434 for platform in table.sortedhash(platforms) do
435 add_collection(update.modules,platform)
436 end
437 for platform in table.sortedhash(platforms) do
438 add_collection(update.fonts,platform)
439 end
440 for engine in table.sortedhash(engines) do
441 for platform in table.sortedhash(platforms) do
442 add_collection(update.engines[engine],platform)
443 end
444 end
445
446 if goodies and type(goodies) == "table" then
447 for goodie in table.sortedhash(goodies) do
448 for platform in table.sortedhash(platforms) do
449 add_collection(update.goodies[goodie],platform)
450 end
451 end
452 end
453
454 local combined = { }
455 local update_repositories = update.repositories
456 for i=1,#update_repositories do
457 local repository = update_repositories[i]
458 if repositories[repository] then
459 for _, v in table.sortedhash(individual) do
460 local archive, destination = v[1], v[2]
461 local cd = combined[destination]
462 if not cd then
463 cd = { }
464 combined[destination] = cd
465 end
466 cd[#cd+1] = format("%s/%s/%s",states.get('rsync.module'),repository,archive)
467 end
468 end
469 end
470 for destination, archive in table.sortedhash(combined) do
471 local archives, command = concat(archive," "), ""
472 local normalflags, deleteflags = states.get("rsync.flags.normal"), ""
473 if os.name == "windows" then
474 normalflags = normalflags .. " -L"
475 end
476 local dryrunflags = ""
477 if not environment.argument("force") then
478 dryrunflags = "--dry-run"
479 end
480 if (find(destination,"texmf$") or find(destination,"texmf%-context$") or find(destination,"texmf%-modules$")) and (not environment.argument("keep")) then
481 deleteflags = states.get("rsync.flags.delete")
482 end
483 command = format("%s %s %s %s %s'%s' '%s'", bin, normalflags, deleteflags, dryrunflags, url, archives, drive(destination))
484
485 if not fetched[command] then
486 update.run(command,true)
487 fetched[command] = command
488 end
489 end
490
491 local function update_script(script, platform)
492 local bin = gsub(bin,"\\","/")
493 local texroot = gsub(texroot,"\\","/")
494 platform = update.platforms[platform]
495 if platform then
496 local command
497 if windowsplatform[platform] then
498 bin = drive(bin)
499 texroot = drive(texroot)
500 command = format([[%s -t "%s/texmf-context/scripts/context/lua/%s.lua" "%s/texmf-%s/bin/"]], bin, texroot, script, texroot, platform)
501 else
502 command = format([[%s -tgo --chmod=a+x '%s/texmf-context/scripts/context/lua/%s.lua' '%s/texmf-%s/bin/%s']], bin, texroot, script, texroot, platform, script)
503 end
504 report("updating %s for %s: %s", script, platform, command)
505 update.run(command)
506 end
507 end
508
509 for platform in table.sortedhash(platforms) do
510 for i=1, #update.selfscripts do
511 update_script(update.selfscripts[i],platform)
512 end
513 end
514
515 else
516 report("no valid texroot: %s",texroot)
517 end
518 if not force then
519 report("use --force to really update files")
520 end
521
522 resolvers.load_tree(texroot)
523
524
525 update.run(format('mtxrun --tree="%s" %s --direct --resolve mktexlsr %s',texroot,silent,quiet))
526
527 update.run(format('mtxrun --tree="%s" %s --generate',texroot,silent))
528
529 report("update, done")
530end
531
532function table.fromhash(t)
533 local h = { }
534 for k, v in table.sortedhash(t) do
535 if v then h[#h+1] = k end
536 end
537 return h
538end
539
540
541function update.make()
542
543 report("make, start")
544
545 local force = environment.argument("force")
546 local silent = environment.argument("silent") and "--silent" or ""
547 local quiet = silent == "" and "" or "--quiet"
548 local texroot = update.fullpath(states.get("paths.root"))
549 local engines = states.get('engines')
550 local goodies = states.get('goodies')
551 local platforms = states.get('platforms')
552 local formats = states.get('formats')
553
554 resolvers.load_tree(texroot)
555
556 update.run(format('mtxrun --tree="%s" %s --direct --resolve mktexlsr %s',texroot,silent,quiet))
557 update.run(format('mtxrun --tree="%s" %s --generate',texroot,silent))
558
559 local askedformats = formats
560 local texformats = table.tohash(update.texformats)
561
562 for k,v in table.sortedhash(texformats) do
563 if not askedformats[k] then
564 texformats[k] = nil
565 end
566 end
567
568
569
570
571
572 local formatlist = concat(table.fromhash(texformats), " ")
573 if formatlist ~= "" then
574 for engine in table.sortedhash(engines) do
575 if engine == "luatex" or engine == "luajittex" then
576 update.run(format('mtxrun --tree="%s" %s --script context --autogenerate --make %s',texroot,silent,silent))
577 update.run(format('mtxrun --tree="%s" %s --script context --autogenerate --make --engine=luajittex %s',texroot,silent,silent))
578 else
579
580 update.run(format('mtxrun --tree="%s" --resolve %s --script context --resolve --make %s --engine=%s %s',texroot,silent,silent,engine,formatlist))
581 end
582 end
583 end
584
585
586
587
588 if not force then
589 report("make, use --force to really make formats")
590 end
591
592
593 update.run(format('mtxrun --tree="%s" %s --generate',texroot,silent))
594
595 report("make, done")
596end
597
598scripts.savestate = true
599
600if scripts.savestate then
601
602 states.load("status-of-update.lua")
603
604
605
606 statistics.starttiming(states)
607
608 states.set("info.version",0.1)
609 states.set("info.count",(states.get("info.count") or 0) + 1,1,false)
610 states.set("info.comment","this file contains the settings of the last 'mtxrun --script update' run",false)
611 states.set("info.date",os.date("!%Y-%m-%d %H:%M:%S"))
612
613 states.set("rsync.program", environment.argument("rsync"), "rsync", true)
614 states.set("rsync.server", environment.argument("server"), "contextgarden.net::", true)
615 states.set("rsync.module", environment.argument("module"), "minimals", true)
616 states.set("rsync.flags.normal", environment.argument("flags"), "-rpztlv", true)
617 states.set("rsync.flags.delete", nil, "--delete", true)
618
619 states.set("paths.root", environment.argument("texroot"), "tex", true)
620
621 states.set("context.version", environment.argument("context"), "current", true)
622
623 local valid = table.tohash(update.repositories)
624 for r in gmatch(environment.argument("repository") or "current","([^, ]+)") do
625 if valid[r] then states.set(" ." .. r, true) end
626 end
627
628 local valid = update.engines
629 local engine = environment.argument("engine") or ""
630 if engine == "" then
631 local e = states.get("engines")
632 if not e or not next(e) then
633 engine = update.defaultengine
634 end
635 end
636 if engine ~= "" then
637 for r in gmatch(engine,"([^, ]+)") do
638 if r == "all" then
639 for k, v in next, valid do
640 if k ~= "all" then
641 states.set("engines." .. k, true)
642 end
643 end
644 break
645 elseif valid[r] then
646 states.set("engines." .. r, true)
647 end
648 end
649 end
650
651
652
653 local valid = update.platforms
654 for r in gmatch(environment.argument("platform") or os.platform,"([^, ]+)") do
655 if valid[r] then states.set("platforms." .. r, true) end
656 end
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673 local valid = table.tohash(update.texformats)
674 for r in gmatch(environment.argument("formats") or "","([^, ]+)") do
675 if valid[r] then states.set("formats." .. r, true) end
676 end
677
678
679
680
681
682
683 states.set("formats.cont-en", true)
684 states.set("formats.cont-nl", true)
685
686
687 for r in gmatch(environment.argument("extras") or "","([^, ]+)") do
688 if r ~= "all" and not find(r,"^[a-z]%-") then
689 r = "t-" .. r
690 end
691 states.set("modules." .. r, true)
692 end
693 for r in gmatch(environment.argument("modules") or "","([^, ]+)") do
694 if r ~= "all" and not find(r,"^[a-z]%-") then
695 r = "t-" .. r
696 end
697 states.set("modules." .. r, true)
698 end
699 for r in gmatch(environment.argument("fonts") or "","([^, ]+)") do
700 states.set("fonts." .. r, true)
701 end
702 for r in gmatch(environment.argument("goodies") or "","([^, ]+)") do
703 states.set("goodies." .. r, true)
704 end
705
706 report("state, loaded")
707 report()
708
709end
710
711if environment.argument("state") then
712 environment.setargument("update",true)
713 environment.setargument("force",true)
714 environment.setargument("make",true)
715end
716
717if environment.argument("mingw") then
718 update.rsyncvariant = "mingw"
719elseif environment.argument("cygwin") then
720 update.rsyncvariant = "cygwin"
721end
722
723if environment.argument("update") then
724 update.synchronize()
725 if environment.argument("make") then
726 update.make()
727 end
728elseif environment.argument("make") then
729 update.make()
730elseif environment.argument("exporthelp") then
731 application.export(environment.argument("exporthelp"),environment.files[1])
732else
733 application.help()
734end
735
736if scripts.savestate then
737 statistics.stoptiming(states)
738 states.set("info.runtime",tonumber(statistics.elapsedtime(states)))
739 if environment.argument("force") then
740 states.save()
741 report("state","saved")
742 end
743end
744 |