mnogootex 0.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/actions/setup-ruby/action.yml +34 -0
- data/.github/workflows/main.yml +44 -0
- data/.gitignore +3 -0
- data/.rspec +0 -2
- data/.rubocop.yml +15 -0
- data/CHANGELOG.md +55 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +4 -3
- data/Guardfile +56 -0
- data/README.md +260 -20
- data/Rakefile +25 -4
- data/demo/.mnogootexrc +4 -0
- data/demo/demo.asciicast +114 -0
- data/demo/demo.gif +0 -0
- data/demo/main.tex +5 -0
- data/exe/mnogootex +2 -92
- data/lib/mnogootex/cfg.rb +72 -0
- data/lib/mnogootex/cli.rb +63 -0
- data/lib/mnogootex/job/logger.rb +53 -0
- data/lib/mnogootex/job/porter.rb +63 -0
- data/lib/mnogootex/job/runner.rb +60 -0
- data/lib/mnogootex/job/warden.rb +104 -0
- data/lib/mnogootex/log/level.rb +17 -0
- data/lib/mnogootex/log/levels.yml +29 -0
- data/lib/mnogootex/log/line.rb +14 -0
- data/lib/mnogootex/log/matcher.rb +17 -0
- data/lib/mnogootex/log/matchers.yml +205 -0
- data/lib/mnogootex/log/processor.rb +115 -0
- data/lib/mnogootex/log.rb +23 -0
- data/lib/mnogootex/utils.rb +27 -0
- data/lib/mnogootex/version.rb +3 -1
- data/lib/mnogootex.rb +4 -4
- data/mnogootex.gemspec +43 -18
- data/spec/mnogootex/cfg_spec.rb +54 -0
- data/spec/mnogootex/job/porter_spec.rb +140 -0
- data/spec/mnogootex/job/runner_spec.rb +74 -0
- data/spec/mnogootex/log/processor_spec.rb +203 -0
- data/spec/mnogootex/utils_spec.rb +52 -0
- data/spec/spec_helper.rb +124 -0
- metadata +150 -29
- data/.gitmodules +0 -3
- data/.travis.yml +0 -5
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/lib/mnogootex/configuration.rb +0 -46
- data/lib/mnogootex/job.rb +0 -75
data/demo/demo.asciicast
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
{"version": 2, "width": 92, "height": 26, "timestamp": 1637333757, "env": {"SHELL": "/bin/zsh", "TERM": "alacritty"}}
|
2
|
+
[0.017014, "o", "\u001b[?2004h$ "]
|
3
|
+
[0.816219, "o", "m"]
|
4
|
+
[0.880306, "o", "n"]
|
5
|
+
[1.120106, "o", "o"]
|
6
|
+
[1.328005, "o", "g"]
|
7
|
+
[1.431937, "o", "o"]
|
8
|
+
[1.560266, "o", "o"]
|
9
|
+
[1.664007, "o", "t"]
|
10
|
+
[1.824229, "o", "e"]
|
11
|
+
[2.096341, "o", "x"]
|
12
|
+
[2.208064, "o", " "]
|
13
|
+
[2.416225, "o", "b"]
|
14
|
+
[2.488119, "o", "u"]
|
15
|
+
[2.528114, "o", "i"]
|
16
|
+
[2.775867, "o", "l"]
|
17
|
+
[2.927851, "o", "d"]
|
18
|
+
[3.048131, "o", " "]
|
19
|
+
[3.18416, "o", "m"]
|
20
|
+
[3.30414, "o", "a"]
|
21
|
+
[3.360091, "o", "i"]
|
22
|
+
[3.463861, "o", "n"]
|
23
|
+
[3.664082, "o", "."]
|
24
|
+
[3.768087, "o", "t"]
|
25
|
+
[3.872092, "o", "e"]
|
26
|
+
[4.072065, "o", "x"]
|
27
|
+
[4.19206, "o", "\r\n\u001b[?2004l\r"]
|
28
|
+
[4.610511, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
29
|
+
[4.630807, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
30
|
+
[4.652109, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
31
|
+
[4.672831, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
32
|
+
[4.693597, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
33
|
+
[4.71437, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
34
|
+
[4.735132, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
35
|
+
[4.755441, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
36
|
+
[4.776142, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;33;49m⣽\u001b[0m\u001b[0;33;49m⣽\u001b[0m\r"]
|
37
|
+
[4.796857, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;33;49m⣻\u001b[0m\u001b[0;33;49m⣻\u001b[0m\r"]
|
38
|
+
[4.817161, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;33;49m⢿\u001b[0m\u001b[0;33;49m⢿\u001b[0m\r"]
|
39
|
+
[4.837458, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;33;49m⡿\u001b[0m\u001b[0;33;49m⡿\u001b[0m\r"]
|
40
|
+
[4.857789, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;33;49m⣟\u001b[0m\u001b[0;33;49m⣟\u001b[0m\r"]
|
41
|
+
[4.878122, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣯\u001b[0m\u001b[0;33;49m⣯\u001b[0m\r"]
|
42
|
+
[4.898448, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;33;49m⣷\u001b[0m\u001b[0;33;49m⣷\u001b[0m\r"]
|
43
|
+
[4.918754, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣾\u001b[0m\r"]
|
44
|
+
[4.939493, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;33;49m⣽\u001b[0m\u001b[0;33;49m⣽\u001b[0m\r"]
|
45
|
+
[4.960303, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;33;49m⣻\u001b[0m\u001b[0;33;49m⣻\u001b[0m\r"]
|
46
|
+
[4.980995, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;33;49m⢿\u001b[0m\u001b[0;33;49m⢿\u001b[0m\r"]
|
47
|
+
[5.001721, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;33;49m⡿\u001b[0m\u001b[0;33;49m⡿\u001b[0m\r"]
|
48
|
+
[5.022407, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;33;49m⣟\u001b[0m\u001b[0;33;49m⣟\u001b[0m\r"]
|
49
|
+
[5.042661, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;33;49m⣯\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
50
|
+
[5.062924, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;33;49m⣷\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
51
|
+
[5.083219, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
52
|
+
[5.10347, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;33;49m⣽\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
53
|
+
[5.123727, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
54
|
+
[5.143997, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
55
|
+
[5.164261, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
56
|
+
[5.18454, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
57
|
+
[5.204898, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
58
|
+
[5.225157, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
59
|
+
[5.245427, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
60
|
+
[5.265709, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
61
|
+
[5.285988, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
62
|
+
[5.306276, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
63
|
+
[5.326587, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
64
|
+
[5.346862, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
65
|
+
[5.367137, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
66
|
+
[5.387471, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
67
|
+
[5.407789, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
68
|
+
[5.428094, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
69
|
+
[5.448427, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
70
|
+
[5.468685, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
71
|
+
[5.48902, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
72
|
+
[5.509286, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
73
|
+
[5.529559, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
74
|
+
[5.549834, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
75
|
+
[5.570114, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
76
|
+
[5.590495, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
77
|
+
[5.610773, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
78
|
+
[5.631053, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
79
|
+
[5.651328, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
80
|
+
[5.671634, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
81
|
+
[5.691885, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
82
|
+
[5.712144, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
83
|
+
[5.732409, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
84
|
+
[5.752725, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
85
|
+
[5.77299, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
86
|
+
[5.793269, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
87
|
+
[5.81354, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
88
|
+
[5.833802, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
89
|
+
[5.854091, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
90
|
+
[5.874667, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
91
|
+
[5.894903, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
92
|
+
[5.915157, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
93
|
+
[5.935432, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
94
|
+
[5.955706, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
95
|
+
[5.975985, "o", "Runners: \u001b[0;33;49m⢿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
96
|
+
[5.996281, "o", "Runners: \u001b[0;33;49m⡿\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
97
|
+
[6.016565, "o", "Runners: \u001b[0;33;49m⣟\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
98
|
+
[6.03685, "o", "Runners: \u001b[0;33;49m⣯\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
99
|
+
[6.057124, "o", "Runners: \u001b[0;33;49m⣷\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
100
|
+
[6.077406, "o", "Runners: \u001b[0;33;49m⣾\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
101
|
+
[6.097789, "o", "Runners: \u001b[0;33;49m⣽\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
102
|
+
[6.118073, "o", "Runners: \u001b[0;33;49m⣻\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r"]
|
103
|
+
[6.138511, "o", "Runners: \u001b[0;32;49m⣯\u001b[0m\u001b[0;31;49m⣾\u001b[0m\u001b[0;31;49m⢿\u001b[0m\r\r\nOutcome:\r\n"]
|
104
|
+
[6.138712, "o", " \u001b[0;32;49m✔\u001b[0m scrarticle\r\n \u001b[0;31;49m✘\u001b[0m article\r\n"]
|
105
|
+
[6.139693, "o", " \u001b[0;97;49mLatexmk: This is Latexmk, John Collins, 21 September 2021, version: 4.75.\u001b[0m\r\n \u001b[0;97;49mThis is pdfTeX, Version 3.141592653-2.6-1.40.23 (TeX Live 2021) (preloaded format=latex)\u001b[0m\r\n \u001b[0;91;49m! Undefined control sequence.\u001b[0m\r\n \u001b[0;91;49ml.4 Let's port my \\KOMAScript\u001b[0m\r\n \u001b[0;91;49m \\ article!\u001b[0m\r\n \u001b[0;97;49mOutput written on main.dvi (1 page, 424 bytes).\u001b[0m\r\n \u001b[0;91;49mLatexmk: Errors, so I did not complete making targets\u001b[0m\r\n \u001b[0;31;49m✘\u001b[0m book\r\n"]
|
106
|
+
[6.140624, "o", " \u001b[0;97;49mLatexmk: This is Latexmk, John Collins, 21 September 2021, version: 4.75.\u001b[0m\r\n \u001b[0;97;49mThis is pdfTeX, Version 3.141592653-2.6-1.40.23 (TeX Live 2021) (preloaded format=latex)\u001b[0m\r\n \u001b[0;91;49m! Undefined control sequence.\u001b[0m\r\n \u001b[0;91;49ml.3 \\abstract\u001b[0m\r\n \u001b[0;91;49m {Simply put, my article is awesome.}\u001b[0m\r\n \u001b[0;91;49m! Undefined control sequence.\u001b[0m\r\n \u001b[0;91;49ml.4 Let's port my \\KOMAScript\u001b[0m\r\n"]
|
107
|
+
[6.140752, "o", " \u001b[0;91;49m \\ article!\u001b[0m\r\n \u001b[0;97;49mOutput written on main.dvi (1 page, 324 bytes).\u001b[0m\r\n \u001b[0;91;49mLatexmk: Errors, so I did not complete making targets\u001b[0m\r\n"]
|
108
|
+
[6.145564, "o", "\u001b[?2004h$ "]
|
109
|
+
[9.623727, "o", "e"]
|
110
|
+
[9.720091, "o", "x"]
|
111
|
+
[9.904131, "o", "i"]
|
112
|
+
[10.016153, "o", "t"]
|
113
|
+
[10.496203, "o", "\r\n\u001b[?2004l\r"]
|
114
|
+
[10.49658, "o", "exit\r\n"]
|
data/demo/demo.gif
ADDED
Binary file
|
data/demo/main.tex
ADDED
data/exe/mnogootex
CHANGED
@@ -1,96 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'mnogootex'
|
5
5
|
|
6
|
-
|
7
|
-
# require 'pathname'
|
8
|
-
|
9
|
-
# target = ARGV[0]
|
10
|
-
# target_path = File.expand_path target
|
11
|
-
# cfg = Mnogootex::Configuration.new
|
12
|
-
# cfg.load target_path
|
13
|
-
|
14
|
-
require 'tmpdir'
|
15
|
-
require 'fileutils'
|
16
|
-
require 'open3'
|
17
|
-
|
18
|
-
require 'colorize'
|
19
|
-
|
20
|
-
target = ARGV[0]
|
21
|
-
|
22
|
-
|
23
|
-
raise "No parameters given." if ARGV.length.zero?
|
24
|
-
|
25
|
-
if ARGV.length == 3
|
26
|
-
raise "Unknown command." unless %w{show view open}.include? ARGV[1].downcase
|
27
|
-
job = Mnogootex::Job.new cls: ARGV[2], target: File.expand_path(target)
|
28
|
-
pdf = Dir.glob("#{job.tmp_dirname}/*.pdf").first
|
29
|
-
raise "PDF non esiste." unless File.exist? pdf
|
30
|
-
`command -v open >/dev/null && open #{pdf} || xdg-open #{pdf}`
|
31
|
-
exit
|
32
|
-
end
|
33
|
-
|
34
|
-
puts "Mnogootex v#{Mnogootex::VERSION}"
|
35
|
-
|
36
|
-
main_path = File.expand_path(target)
|
37
|
-
main_basename = File.basename main_path
|
38
|
-
main_dirname = File.dirname main_path
|
39
|
-
|
40
|
-
cfg = Mnogootex::Configuration.new
|
41
|
-
cfg.load main_dirname
|
42
|
-
|
43
|
-
raise "File non esiste." unless File.exist? main_path
|
44
|
-
|
45
|
-
@documentclasses = cfg['compile_with']
|
46
|
-
|
47
|
-
$jobs = []
|
48
|
-
$threads = []
|
49
|
-
$draw_threads = []
|
50
|
-
|
51
|
-
$threads = []
|
52
|
-
|
53
|
-
$anim = cfg['animation'].freeze
|
54
|
-
|
55
|
-
STDOUT.sync = true
|
56
|
-
|
57
|
-
def draw_status
|
58
|
-
icons = $jobs.map do |j|
|
59
|
-
icon = $anim[j.ticks % $anim.length]
|
60
|
-
case j.thread.status
|
61
|
-
when 'sleep', 'run', 'aborting'
|
62
|
-
icon.yellow
|
63
|
-
when false, nil # exited (normally or w/ error)
|
64
|
-
j.success? ? icon.green : icon.red
|
65
|
-
end
|
66
|
-
end
|
67
|
-
print ' Jobs: ' + icons.join + "\r"
|
68
|
-
end
|
69
|
-
|
70
|
-
draw_status
|
71
|
-
|
72
|
-
@documentclasses.each_with_index do |cls, index|
|
73
|
-
job = Mnogootex::Job.new cls: cls, target: main_path
|
74
|
-
job.setup
|
75
|
-
job.run
|
76
|
-
|
77
|
-
$jobs << job
|
78
|
-
|
79
|
-
$draw_threads << job.tick_thread
|
80
|
-
$threads << job.thread
|
81
|
-
end
|
82
|
-
|
83
|
-
$threads.map(&:join)
|
84
|
-
$draw_threads.map(&:join)
|
85
|
-
|
86
|
-
puts
|
87
|
-
|
88
|
-
puts ' Details:'
|
89
|
-
$jobs.each do |job|
|
90
|
-
if job.success?
|
91
|
-
puts ' ' + "✔".green + ' ' + File.basename(job.cls)
|
92
|
-
else
|
93
|
-
puts ' ' + "✘".red + ' ' + File.basename(job.cls)
|
94
|
-
puts job.log[2..-2].join.gsub(/^/,' '*6).chomp.red
|
95
|
-
end
|
96
|
-
end
|
6
|
+
Mnogootex::CLI.start ARGV
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module Mnogootex
|
7
|
+
module Cfg
|
8
|
+
BASENAME = '.mnogootexrc'
|
9
|
+
DEFAULTS = {
|
10
|
+
'jobs' => [],
|
11
|
+
'spinner' => '⣾⣽⣻⢿⡿⣟⣯⣷',
|
12
|
+
'work_path' => nil,
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def self.load_descending(pathname:, basename:)
|
16
|
+
pathname.realpath.descend.
|
17
|
+
map { |path| path.join(basename) }.
|
18
|
+
select(&:exist?).reject(&:zero?).
|
19
|
+
map { |path| YAML.load_file(path) }.
|
20
|
+
reduce(&:merge!)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.recombobulate(*args)
|
24
|
+
try_args(*args) || try_link(*args) || try_cfgs(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
private
|
29
|
+
|
30
|
+
def split_jobs_and_flags(args)
|
31
|
+
# TODO: some kind of validation?
|
32
|
+
flags = args.drop_while { |arg| !arg.start_with?('-') }
|
33
|
+
jobs = args.take_while { |arg| !arg.start_with?('-') }
|
34
|
+
[(jobs unless jobs.empty?), (flags unless flags.empty?)]
|
35
|
+
end
|
36
|
+
|
37
|
+
def try_args(*args)
|
38
|
+
main = Pathname.new(args.fetch(-1, ''))
|
39
|
+
return unless main.file?
|
40
|
+
|
41
|
+
main = main.realpath
|
42
|
+
cfg = load_descending(pathname: main.dirname, basename: BASENAME)
|
43
|
+
jobs, flags = split_jobs_and_flags(args[0..-2])
|
44
|
+
|
45
|
+
[jobs, flags, main, cfg]
|
46
|
+
end
|
47
|
+
|
48
|
+
def try_link(*args)
|
49
|
+
link = Pathname.pwd.ascend.map { |p| p.join('.mnogootex.src') }.detect(&:symlink?)
|
50
|
+
return if link.nil?
|
51
|
+
|
52
|
+
main = link.readlink.realpath
|
53
|
+
cfg = load_descending(pathname: main.dirname, basename: BASENAME)
|
54
|
+
jobs, flags = split_jobs_and_flags(args)
|
55
|
+
|
56
|
+
[jobs, flags, main, cfg]
|
57
|
+
end
|
58
|
+
|
59
|
+
def try_cfgs(*args)
|
60
|
+
yaml = Pathname.pwd.ascend.map { |p| p.join('.mnogootexrc') }.detect(&:file?)
|
61
|
+
return if yaml.nil?
|
62
|
+
|
63
|
+
cfg = load_descending(pathname: yaml.dirname, basename: BASENAME)
|
64
|
+
main = yaml.dirname.join(cfg.fetch('main', ''))
|
65
|
+
main = main.file? ? main.realpath : nil
|
66
|
+
jobs, flags = split_jobs_and_flags(args)
|
67
|
+
|
68
|
+
[jobs, flags, main, cfg]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
require 'mnogootex/utils'
|
7
|
+
require 'mnogootex/job/warden'
|
8
|
+
require 'mnogootex/job/porter'
|
9
|
+
require 'mnogootex/cfg'
|
10
|
+
|
11
|
+
module Mnogootex
|
12
|
+
class CLI < Thor
|
13
|
+
desc 'exec [JOB ...] [FLAG ...] ROOT',
|
14
|
+
'Execute latexmk with FLAGs on each JOB for ROOT document'
|
15
|
+
def exec(*args)
|
16
|
+
execute_latexmk(*args, default_flags: [])
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'build [JOB ...] [FLAG ...] ROOT',
|
20
|
+
'Build each JOB for ROOT document'
|
21
|
+
def build(*args)
|
22
|
+
execute_latexmk(*args, default_flags: ['-interaction=nonstopmode'])
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'open [JOB ...] [FLAG ...] ROOT',
|
26
|
+
'(Build and) open the artifact of each JOB for ROOT document'
|
27
|
+
def open(*args)
|
28
|
+
execute_latexmk(*args, default_flags: ['-interaction=nonstopmode', '-pv'])
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'clean [JOB ...] [FLAG ...] ROOT',
|
32
|
+
'Delete nonessential files of each JOB for ROOT document'
|
33
|
+
def clean(*args)
|
34
|
+
execute_latexmk(*args, default_flags: ['-c'])
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'clobber [JOB ...] [FLAG ...] ROOT',
|
38
|
+
'Delete nonessential files and artifacts of each JOB for ROOT document'
|
39
|
+
def clobber(*args)
|
40
|
+
execute_latexmk(*args, default_flags: ['-C'])
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'help [COMMAND]',
|
44
|
+
'Describe available commands or one specific COMMAND'
|
45
|
+
def help(*args)
|
46
|
+
super
|
47
|
+
|
48
|
+
puts <<~EXTRA_HELP
|
49
|
+
JOBs are document class names. The default is the whole list in your configuration file.
|
50
|
+
FLAGs are options passed to latexmk. Please refer to `latexmk -help` for details.
|
51
|
+
EXTRA_HELP
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def execute_latexmk(*args, default_flags: [])
|
57
|
+
jobs, flags, main, cfg = Mnogootex::Cfg.recombobulate(*args)
|
58
|
+
cfg = Mnogootex::Cfg::DEFAULTS.merge(cfg).merge({ 'jobs' => jobs }.compact)
|
59
|
+
flags = [*default_flags, *flags]
|
60
|
+
Mnogootex::Job::Warden.new(source: main, configuration: cfg, flags: flags).start
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
module Mnogootex
|
6
|
+
module Job
|
7
|
+
class Logger < Thread
|
8
|
+
def initialize(spinner:, processor:, runners:, porters:)
|
9
|
+
super do
|
10
|
+
while runners.any?(&:alive?)
|
11
|
+
self.class.print_status(runners: runners, spinner: spinner)
|
12
|
+
sleep 0.02 # 50 fps
|
13
|
+
end
|
14
|
+
self.class.print_status(runners: runners, spinner: spinner)
|
15
|
+
puts
|
16
|
+
self.class.print_outcome(runners: runners, porters: porters, processor: processor)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def print_status(runners:, spinner:)
|
22
|
+
spinners_frames = []
|
23
|
+
runners.each do |runner|
|
24
|
+
spinner_frame = spinner[runner.count_lines % spinner.size]
|
25
|
+
spinners_frames << colour_by_state(spinner_frame, runner)
|
26
|
+
end
|
27
|
+
print "Runners: #{spinners_frames.join}\r"
|
28
|
+
end
|
29
|
+
|
30
|
+
def print_outcome(runners:, porters:, processor:)
|
31
|
+
puts 'Outcome:'
|
32
|
+
porters.zip(runners).each do |porter, runner|
|
33
|
+
outcome_icon = runner.successful? ? '✔'.green : '✘'.red
|
34
|
+
puts " #{outcome_icon} #{porter.hid}"
|
35
|
+
puts processor.call(runner.log_lines) unless runner.successful?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def colour_by_state(string, runner)
|
42
|
+
if runner.alive?
|
43
|
+
string.yellow
|
44
|
+
elsif runner.successful?
|
45
|
+
string.green
|
46
|
+
else
|
47
|
+
string.red
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'tmpdir'
|
5
|
+
|
6
|
+
require 'mnogootex/utils'
|
7
|
+
|
8
|
+
module Mnogootex
|
9
|
+
module Job
|
10
|
+
class Porter
|
11
|
+
attr_reader :hid
|
12
|
+
|
13
|
+
def initialize(hid:, source_path:, work_path: nil)
|
14
|
+
@source_path = Pathname.new(source_path).realpath
|
15
|
+
@work_path = calc_work_path(work_path).tap(&:mkpath).realpath
|
16
|
+
@hid = hid
|
17
|
+
end
|
18
|
+
|
19
|
+
def target_dir
|
20
|
+
@target_dir ||= @work_path.join(hid)
|
21
|
+
end
|
22
|
+
|
23
|
+
def target_path
|
24
|
+
@target_path ||= target_dir.join(@source_path.basename)
|
25
|
+
end
|
26
|
+
|
27
|
+
def clobber
|
28
|
+
target_dir.rmtree if target_dir.directory?
|
29
|
+
end
|
30
|
+
|
31
|
+
def provide
|
32
|
+
target_dir.mkpath
|
33
|
+
providable_files = @source_path.dirname.children
|
34
|
+
providable_files.reject!(&@work_path.method(:==))
|
35
|
+
FileUtils.cp_r providable_files, target_dir
|
36
|
+
remove_configuration(target_dir)
|
37
|
+
create_link_to_source(target_dir)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def remove_configuration(folder_path)
|
43
|
+
path = folder_path.join('.mnogootexrc')
|
44
|
+
path.delete if path.file?
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_link_to_source(folder_path)
|
48
|
+
path = folder_path.join('.mnogootex.src')
|
49
|
+
path.make_symlink(@source_path) unless path.symlink?
|
50
|
+
end
|
51
|
+
|
52
|
+
def calc_work_path(path)
|
53
|
+
return Pathname.new(path) unless path.nil?
|
54
|
+
|
55
|
+
Pathname.new(Dir.tmpdir).join('mnogootex', source_id)
|
56
|
+
end
|
57
|
+
|
58
|
+
def source_id
|
59
|
+
@source_id ||= Utils.short_md5(@source_path.to_s)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'io/wait'
|
5
|
+
|
6
|
+
module Mnogootex
|
7
|
+
module Job
|
8
|
+
class Runner
|
9
|
+
POLLING_TIMEOUT = 0.02
|
10
|
+
|
11
|
+
attr_reader :hid, :log_lines
|
12
|
+
|
13
|
+
def initialize(cmd:, chdir:)
|
14
|
+
@log_lines = []
|
15
|
+
_, @stream, @thread = Open3.popen2e(*cmd, chdir: chdir)
|
16
|
+
@poller = start_poller
|
17
|
+
end
|
18
|
+
|
19
|
+
def alive?
|
20
|
+
@poller.alive?
|
21
|
+
end
|
22
|
+
|
23
|
+
def successful?
|
24
|
+
@poller.value.exitstatus.zero?
|
25
|
+
end
|
26
|
+
|
27
|
+
def count_lines
|
28
|
+
return log_lines.size unless alive?
|
29
|
+
|
30
|
+
@ticks = [@ticks || -1, log_lines.size - 1].min + 1
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def start_poller
|
36
|
+
Thread.new do
|
37
|
+
polling_loop
|
38
|
+
|
39
|
+
# NOTE: waits on @thread and returns its value
|
40
|
+
@thread.value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def polling_loop
|
45
|
+
loop do
|
46
|
+
if @stream.wait_readable(POLLING_TIMEOUT).nil?
|
47
|
+
# If the stream timeouts and the thread is dead we expect no nore data.
|
48
|
+
# This happens on commands like `latexmk -pv` which fork other processes.
|
49
|
+
break unless @thread.alive?
|
50
|
+
else
|
51
|
+
# If we reach EOF, we expect no more data.
|
52
|
+
break if (line = @stream.gets).nil?
|
53
|
+
|
54
|
+
log_lines << line
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
require 'mnogootex/log'
|
6
|
+
require 'mnogootex/log/processor'
|
7
|
+
require 'mnogootex/job/porter'
|
8
|
+
require 'mnogootex/job/runner'
|
9
|
+
require 'mnogootex/job/logger'
|
10
|
+
|
11
|
+
module Mnogootex
|
12
|
+
module Job
|
13
|
+
class Warden
|
14
|
+
LATEXMK_PATH = 'latexmk'
|
15
|
+
|
16
|
+
def initialize(source:, configuration:, flags:)
|
17
|
+
@source = source
|
18
|
+
@configuration = configuration
|
19
|
+
@flags = flags
|
20
|
+
|
21
|
+
@processor = nil
|
22
|
+
@porters = []
|
23
|
+
@runners = []
|
24
|
+
@logger = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
init_processor
|
29
|
+
init_porters
|
30
|
+
exec_porters
|
31
|
+
init_and_exec_runners
|
32
|
+
init_and_exec_logger
|
33
|
+
@logger.join
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def init_porters
|
39
|
+
@configuration['jobs'].each do |cls|
|
40
|
+
@porters << Mnogootex::Job::Porter.new(
|
41
|
+
hid: cls,
|
42
|
+
source_path: @source,
|
43
|
+
work_path: @configuration['work_path'],
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def exec_porters
|
49
|
+
@porters.each do |porter|
|
50
|
+
# porter.clobber
|
51
|
+
porter.provide
|
52
|
+
transformer(porter.hid, porter.target_path)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def init_and_exec_runners
|
57
|
+
@runners = @porters.map do |porter|
|
58
|
+
Mnogootex::Job::Runner.new(
|
59
|
+
cmd: commandline(porter.target_path),
|
60
|
+
chdir: porter.target_dir,
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def init_processor
|
66
|
+
@processor = Log::Processor.new(
|
67
|
+
matchers: Mnogootex::Log::DEFAULT_MATCHERS,
|
68
|
+
levels: Mnogootex::Log::DEFAULT_LEVELS,
|
69
|
+
min_level: :info,
|
70
|
+
colorize: true,
|
71
|
+
indent_width: 4,
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def init_and_exec_logger
|
76
|
+
@logger = Mnogootex::Job::Logger.new(
|
77
|
+
spinner: @configuration['spinner'],
|
78
|
+
processor: @processor.method(:run),
|
79
|
+
runners: @runners,
|
80
|
+
porters: @porters,
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
# TODO: generalize, integrate with Runner
|
85
|
+
def commandline(target_pathname)
|
86
|
+
[
|
87
|
+
LATEXMK_PATH,
|
88
|
+
*@flags,
|
89
|
+
target_pathname.basename.to_s
|
90
|
+
]
|
91
|
+
end
|
92
|
+
|
93
|
+
# TODO: generalize, integrate with Porter
|
94
|
+
def transformer(new_class_name, target_pathname)
|
95
|
+
old_code = target_pathname.read
|
96
|
+
new_code = old_code.sub(
|
97
|
+
/\\documentclass(\[.*?\])?{.*?}/,
|
98
|
+
"\\documentclass{#{new_class_name}}",
|
99
|
+
)
|
100
|
+
target_pathname.write(new_code)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mnogootex
|
4
|
+
module Log
|
5
|
+
# This data structure represents a log level usually referred to
|
6
|
+
# by its {name}. It has a numeric {priority} and a {color} used
|
7
|
+
# for rendering.
|
8
|
+
#
|
9
|
+
# @!attribute priority
|
10
|
+
# @return [Numeric] the numeric priority of the log level
|
11
|
+
# @!attribute name
|
12
|
+
# @return [Symbol] the human readable name of the log level
|
13
|
+
# @!attribute color
|
14
|
+
# @return [Symbol] the color visually representing the {priority}
|
15
|
+
Level = Struct.new(:priority, :name, :color)
|
16
|
+
end
|
17
|
+
end
|