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 ["mswin"] = "mswin",
162 ["windows"] = "mswin",
163 ["win32"] = "mswin",
164 ["win"] = "mswin",
165
166 ["mswin-64"] = "win64",
167 ["windows-64"] = "win64",
168 ["win64"] = "win64",
169
170 ["linux"] = "linux",
171 ["linux-32"] = "linux",
172 ["linux32"] = "linux",
173
174 ["linux-64"] = "linux-64",
175 ["linux64"] = "linux-64",
176
177 ["linuxmusl-64"] = "linuxmusl-64",
178
179 ["linux-armhf"] = "linux-armhf",
180
181 ["openbsd"] = "openbsd7.0",
182 ["openbsd-i386"] = "openbsd7.0",
183 ["openbsd-amd64"] = "openbsd7.0-amd64",
184
185 ["freebsd"] = "freebsd",
186 ["freebsd-i386"] = "freebsd",
187 ["freebsd-amd64"] = "freebsd-amd64",
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 ["macosx"] = "osx-64",
207 ["osx"] = "osx-64",
208 ["osx-64"] = "osx-64",
209 ["osx-arm"] = "osx-arm64",
210 ["osx-arm64"] = "osx-arm64",
211
212
213
214
215
216
217 ["unknown"] = "unknown",
218}
219
220local windowsplatform = {
221 ["mswin"] = true,
222 ["win32"] = true,
223 ["win64"] = true,
224}
225
226update.selfscripts = {
227 "mtxrun",
228
229}
230
231
232
233update.modules = {
234}
235
236update.fonts = {
237}
238
239function update.run(str)
240
241
242 os.setenv("engine",nil)
243 if environment.argument("force") then
244 report("run, %s",str)
245 os.execute(str)
246 else
247 report("dry run, %s",str)
248 end
249end
250
251function update.fullpath(path)
252 if file.is_rootbased_path(path) then
253 return path
254 else
255 return lfs.currentdir() .. "/" .. path
256 end
257end
258
259local function drive(d)
260 if update.rsyncvariant == "cygwin" then
261 d = gsub(d,[[([a-zA-Z]):/]], "/cygdrive/%1/")
262 else
263 d = gsub(d,[[([a-zA-Z]):/]], "/%1/")
264 end
265 return d
266end
267
268function update.synchronize()
269
270 report("update, start")
271
272 local texroot = update.fullpath(states.get("paths.root"))
273 local engines = states.get('engines') or { }
274 local platforms = states.get('platforms') or { }
275 local repositories = states.get('repositories')
276 local bin = states.get("rsync.program")
277 local url = states.get("rsync.server")
278 local version = states.get("context.version")
279 local modules = states.get("modules")
280 local fonts = states.get("fonts")
281 local goodies = states.get("goodies")
282 local force = environment.argument("force")
283 local silent = environment.argument("silent") and "--silent" or ""
284 local quiet = silent == "" and "" or "--quiet"
285
286 bin = gsub(bin,"\\","/")
287
288 if not find(url,"::$") then url = url .. "::" end
289 local ok = lfs.attributes(texroot,"mode") == "directory"
290 if not ok and force then
291 dir.mkdirs(texroot)
292 ok = lfs.attributes(texroot,"mode") == "directory"
293 end
294
295 if force then
296 dir.mkdirs(format("%s/%s", texroot, "texmf-cache"))
297 dir.mkdirs(format("%s/%s", texroot, "texmf-local"))
298 dir.mkdirs(format("%s/%s", texroot, "texmf-project"))
299 dir.mkdirs(format("%s/%s", texroot, "texmf-fonts"))
300 dir.mkdirs(format("%s/%s", texroot, "texmf-modules"))
301 end
302
303 if ok or not force then
304
305 local fetched, individual, osplatform = { }, { }, os.platform
306
307
308
309 local function collection_to_list_of_folders(collection, platform)
310 local archives = {}
311 for i=1,#collection do
312 local archive = collection[i][1]
313 archive = gsub(archive,"<platform>",platform)
314 archive = gsub(archive,"<version>",version)
315 archives[#archives+1] = archive
316 end
317 return archives
318 end
319
320
321
322
323
324
325
326 local function list_of_folders_to_rsync_string(list_of_folders)
327 local repository = 'current'
328 local prefix = format("%s/%s/", states.get('rsync.module'), repository)
329
330 return prefix .. concat(list_of_folders, format(" %s", prefix))
331 end
332
333
334
335
336
337
338
339
340 local function get_list_of_files_from_rsync(list_of_folders)
341
342 local temp_file = "rsync.tmp.txt"
343
344 local folders = {}
345 local command = format("%s %s'%s' > %s", bin, url, list_of_folders_to_rsync_string(list_of_folders), temp_file)
346 os.execute(command)
347
348 local data = io.loaddata(temp_file) or ""
349
350 for chmod, s in gmatch(data,"([d%-][rwxst%-]+).-(%S+)[\n\r]") do
351
352 if s ~= '.' and #chmod >= 10 then
353 folders[#folders+1] = s
354 end
355 end
356
357 os.remove(temp_file)
358 return folders
359 end
360
361
362
363 local available_platforms = get_list_of_files_from_rsync({"bin/luatex/"})
364
365 report("available platforms: % t",table.sorted(available_platforms))
366
367 if modules and type(modules) == "table" then
368
369
370
371
372 local available_modules = get_list_of_files_from_rsync({"modules/"})
373 local asked = table.copy(modules)
374 asked.all = nil
375 report("available modules: %s",#available_modules)
376 for i=1,#available_modules do
377 local s = available_modules[i]
378 if modules.all or modules[s] then
379 update.modules[#update.modules+1] = { format("modules/%s/",s), "texmf-modules" }
380 report("+ %s",s)
381 else
382 report(" %s",s)
383 end
384 asked[s] = nil
385 end
386 if next(asked) then
387 report("skipping unknown modules: %s",concat(table.sortedkeys(asked),", "))
388 end
389 end
390
391
392
393 if fonts and type(fonts) == "table" then
394 local available_fonts = get_list_of_files_from_rsync({"fonts/extra/"})
395 local asked = table.copy(fonts)
396 asked.all = nil
397 for i=1,#available_fonts do
398 local s = available_fonts[i]
399 if fonts.all or fonts[s] then
400 update.fonts[#update.fonts+1] = { format("fonts/extra/%s/",s), "texmf" }
401 end
402 asked[s] = nil
403 end
404 if next(asked) then
405 report("skipping unknown fonts: %s",concat(table.sortedkeys(asked),", "))
406 end
407 end
408
409 local function add_collection(collection,platform)
410 if collection and platform then
411 platform = update.platforms[platform]
412 if platform then
413 for i=1,#collection do
414 local c = collection[i]
415 local archive = gsub(c[1],"<platform>",platform)
416 local destination = format("%s/%s", texroot, gsub(c[2],"<platform>", platform))
417 destination = gsub(destination,"\\","/")
418 archive = gsub(archive,"<version>",version)
419 if osplatform == "windows" or osplatform == "mswin" or osplatform == "win64" then
420 destination = drive(destination)
421 end
422 individual[#individual+1] = { archive, destination }
423 end
424 end
425 end
426 end
427
428 for platform in table.sortedhash(platforms) do
429 add_collection(update.base,platform)
430 end
431 for platform in table.sortedhash(platforms) do
432 add_collection(update.modules,platform)
433 end
434 for platform in table.sortedhash(platforms) do
435 add_collection(update.fonts,platform)
436 end
437 for engine in table.sortedhash(engines) do
438 for platform in table.sortedhash(platforms) do
439 add_collection(update.engines[engine],platform)
440 end
441 end
442
443 if goodies and type(goodies) == "table" then
444 for goodie in table.sortedhash(goodies) do
445 for platform in table.sortedhash(platforms) do
446 add_collection(update.goodies[goodie],platform)
447 end
448 end
449 end
450
451 local combined = { }
452 local update_repositories = update.repositories
453 for i=1,#update_repositories do
454 local repository = update_repositories[i]
455 if repositories[repository] then
456 for _, v in table.sortedhash(individual) do
457 local archive, destination = v[1], v[2]
458 local cd = combined[destination]
459 if not cd then
460 cd = { }
461 combined[destination] = cd
462 end
463 cd[#cd+1] = format("%s/%s/%s",states.get('rsync.module'),repository,archive)
464 end
465 end
466 end
467 for destination, archive in table.sortedhash(combined) do
468 local archives, command = concat(archive," "), ""
469 local normalflags, deleteflags = states.get("rsync.flags.normal"), ""
470 if os.name == "windows" then
471 normalflags = normalflags .. " -L"
472 end
473 local dryrunflags = ""
474 if not environment.argument("force") then
475 dryrunflags = "--dry-run"
476 end
477 if (find(destination,"texmf$") or find(destination,"texmf%-context$") or find(destination,"texmf%-modules$")) and (not environment.argument("keep")) then
478 deleteflags = states.get("rsync.flags.delete")
479 end
480 command = format("%s %s %s %s %s'%s' '%s'", bin, normalflags, deleteflags, dryrunflags, url, archives, drive(destination))
481
482 if not fetched[command] then
483 update.run(command,true)
484 fetched[command] = command
485 end
486 end
487
488 local function update_script(script, platform)
489 local bin = gsub(bin,"\\","/")
490 local texroot = gsub(texroot,"\\","/")
491 platform = update.platforms[platform]
492 if platform then
493 local command
494 if windowsplatform[platform] then
495 bin = drive(bin)
496 texroot = drive(texroot)
497 command = format([[%s -t "%s/texmf-context/scripts/context/lua/%s.lua" "%s/texmf-%s/bin/"]], bin, texroot, script, texroot, platform)
498 else
499 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)
500 end
501 report("updating %s for %s: %s", script, platform, command)
502 update.run(command)
503 end
504 end
505
506 for platform in table.sortedhash(platforms) do
507 for i=1, #update.selfscripts do
508 update_script(update.selfscripts[i],platform)
509 end
510 end
511
512 else
513 report("no valid texroot: %s",texroot)
514 end
515 if not force then
516 report("use --force to really update files")
517 end
518
519 resolvers.load_tree(texroot)
520
521
522 update.run(format('mtxrun --tree="%s" %s --direct --resolve mktexlsr %s',texroot,silent,quiet))
523
524 update.run(format('mtxrun --tree="%s" %s --generate',texroot,silent))
525
526 report("update, done")
527end
528
529function table.fromhash(t)
530 local h = { }
531 for k, v in table.sortedhash(t) do
532 if v then h[#h+1] = k end
533 end
534 return h
535end
536
537
538function update.make()
539
540 report("make, start")
541
542 local force = environment.argument("force")
543 local silent = environment.argument("silent") and "--silent" or ""
544 local quiet = silent == "" and "" or "--quiet"
545 local texroot = update.fullpath(states.get("paths.root"))
546 local engines = states.get('engines')
547 local goodies = states.get('goodies')
548 local platforms = states.get('platforms')
549 local formats = states.get('formats')
550
551 resolvers.load_tree(texroot)
552
553 update.run(format('mtxrun --tree="%s" %s --direct --resolve mktexlsr %s',texroot,silent,quiet))
554 update.run(format('mtxrun --tree="%s" %s --generate',texroot,silent))
555
556 local askedformats = formats
557 local texformats = table.tohash(update.texformats)
558
559 for k,v in table.sortedhash(texformats) do
560 if not askedformats[k] then
561 texformats[k] = nil
562 end
563 end
564
565
566
567
568
569 local formatlist = concat(table.fromhash(texformats), " ")
570 if formatlist ~= "" then
571 for engine in table.sortedhash(engines) do
572 if engine == "luatex" or engine == "luajittex" then
573 update.run(format('mtxrun --tree="%s" %s --script context --autogenerate --make %s',texroot,silent,silent))
574 update.run(format('mtxrun --tree="%s" %s --script context --autogenerate --make --engine=luajittex %s',texroot,silent,silent))
575 else
576
577 update.run(format('mtxrun --tree="%s" --resolve %s --script context --resolve --make %s --engine=%s %s',texroot,silent,silent,engine,formatlist))
578 end
579 end
580 end
581
582
583
584
585 if not force then
586 report("make, use --force to really make formats")
587 end
588
589
590 update.run(format('mtxrun --tree="%s" %s --generate',texroot,silent))
591
592 report("make, done")
593end
594
595scripts.savestate = true
596
597if scripts.savestate then
598
599 states.load("status-of-update.lua")
600
601
602
603 statistics.starttiming(states)
604
605 states.set("info.version",0.1)
606 states.set("info.count",(states.get("info.count") or 0) + 1,1,false)
607 states.set("info.comment","this file contains the settings of the last 'mtxrun --script update' run",false)
608 states.set("info.date",os.date("!%Y-%m-%d %H:%M:%S"))
609
610 states.set("rsync.program", environment.argument("rsync"), "rsync", true)
611 states.set("rsync.server", environment.argument("server"), "contextgarden.net::", true)
612 states.set("rsync.module", environment.argument("module"), "minimals", true)
613 states.set("rsync.flags.normal", environment.argument("flags"), "-rpztlv", true)
614 states.set("rsync.flags.delete", nil, "--delete", true)
615
616 states.set("paths.root", environment.argument("texroot"), "tex", true)
617
618 states.set("context.version", environment.argument("context"), "current", true)
619
620 local valid = table.tohash(update.repositories)
621 for r in gmatch(environment.argument("repository") or "current","([^, ]+)") do
622 if valid[r] then states.set(" ." .. r, true) end
623 end
624
625 local valid = update.engines
626 local engine = environment.argument("engine") or ""
627 if engine == "" then
628 local e = states.get("engines")
629 if not e or not next(e) then
630 engine = update.defaultengine
631 end
632 end
633 if engine ~= "" then
634 for r in gmatch(engine,"([^, ]+)") do
635 if r == "all" then
636 for k, v in next, valid do
637 if k ~= "all" then
638 states.set("engines." .. k, true)
639 end
640 end
641 break
642 elseif valid[r] then
643 states.set("engines." .. r, true)
644 end
645 end
646 end
647
648
649
650 local valid = update.platforms
651 for r in gmatch(environment.argument("platform") or os.platform,"([^, ]+)") do
652 if valid[r] then states.set("platforms." .. r, true) end
653 end
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670 local valid = table.tohash(update.texformats)
671 for r in gmatch(environment.argument("formats") or "","([^, ]+)") do
672 if valid[r] then states.set("formats." .. r, true) end
673 end
674
675
676
677
678
679
680 states.set("formats.cont-en", true)
681 states.set("formats.cont-nl", true)
682
683
684 for r in gmatch(environment.argument("extras") or "","([^, ]+)") do
685 if r ~= "all" and not find(r,"^[a-z]%-") then
686 r = "t-" .. r
687 end
688 states.set("modules." .. r, true)
689 end
690 for r in gmatch(environment.argument("modules") or "","([^, ]+)") do
691 if r ~= "all" and not find(r,"^[a-z]%-") then
692 r = "t-" .. r
693 end
694 states.set("modules." .. r, true)
695 end
696 for r in gmatch(environment.argument("fonts") or "","([^, ]+)") do
697 states.set("fonts." .. r, true)
698 end
699 for r in gmatch(environment.argument("goodies") or "","([^, ]+)") do
700 states.set("goodies." .. r, true)
701 end
702
703 report("state, loaded")
704 report()
705
706end
707
708if environment.argument("state") then
709 environment.setargument("update",true)
710 environment.setargument("force",true)
711 environment.setargument("make",true)
712end
713
714if environment.argument("mingw") then
715 update.rsyncvariant = "mingw"
716elseif environment.argument("cygwin") then
717 update.rsyncvariant = "cygwin"
718end
719
720if environment.argument("update") then
721 update.synchronize()
722 if environment.argument("make") then
723 update.make()
724 end
725elseif environment.argument("make") then
726 update.make()
727elseif environment.argument("exporthelp") then
728 application.export(environment.argument("exporthelp"),environment.files[1])
729else
730 application.help()
731end
732
733if scripts.savestate then
734 statistics.stoptiming(states)
735 states.set("info.runtime",tonumber(statistics.elapsedtime(states)))
736 if environment.argument("force") then
737 states.save()
738 report("state","saved")
739 end
740end
741 |