mnogootex 0.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|