]>
Commit | Line | Data |
---|---|---|
1 | /*############################################################################# | |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2023 Pakfire development team # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
21 | #include <argp.h> | |
22 | #include <errno.h> | |
23 | #include <stdlib.h> | |
24 | #include <sys/syslog.h> | |
25 | ||
26 | #include "build.h" | |
27 | #include "color.h" | |
28 | #include "command.h" | |
29 | #include "pakfire.h" | |
30 | ||
31 | #include <pakfire/archive.h> | |
32 | #include <pakfire/build.h> | |
33 | #include <pakfire/log_file.h> | |
34 | #include <pakfire/package.h> | |
35 | #include <pakfire/root.h> | |
36 | #include <pakfire/path.h> | |
37 | #include <pakfire/repo.h> | |
38 | ||
39 | #define MAX_MAKEFILES 32 | |
40 | ||
41 | struct cli_local_args { | |
42 | const char* distro; | |
43 | const char* target; | |
44 | enum { | |
45 | BUILD_INTERACTIVE = (1 << 0), | |
46 | BUILD_ENABLE_CCACHE = (1 << 1), | |
47 | BUILD_ENABLE_SNAPSHOT = (1 << 2), | |
48 | BUILD_ENABLE_TESTS = (1 << 3), | |
49 | } flags; | |
50 | ||
51 | // Log file | |
52 | pakfire_log_file* log_file; | |
53 | const char* log_path; | |
54 | ||
55 | // Makefiles | |
56 | char* makefiles[MAX_MAKEFILES]; | |
57 | unsigned int num_makefiles; | |
58 | }; | |
59 | ||
60 | enum { | |
61 | OPT_DISABLE_CCACHE = 1, | |
62 | OPT_DISABLE_SNAPSHOT = 2, | |
63 | OPT_DISABLE_TESTS = 3, | |
64 | OPT_LOG_FILE = 4, | |
65 | OPT_NON_INTERACTIVE = 5, | |
66 | OPT_TARGET = 6, | |
67 | }; | |
68 | ||
69 | static struct argp_option options[] = { | |
70 | { "disable-ccache", OPT_DISABLE_CCACHE, NULL, 0, "Disable the ccache", 0 }, | |
71 | { "disable-snapshot", OPT_DISABLE_SNAPSHOT, NULL, 0, "Do not use the snapshot", 0 }, | |
72 | { "disable-tests", OPT_DISABLE_TESTS, NULL, 0, "Do not run tests", 0 }, | |
73 | { "log-file", OPT_LOG_FILE, "PATH", 0, "Writes the log to a file", 0 }, | |
74 | { "non-interactive", OPT_NON_INTERACTIVE, NULL, 0, "Run the build non-interactively", 0 }, | |
75 | { "target", OPT_TARGET, "TARGET", 0, "Output all packages into this directory", 0 }, | |
76 | { NULL }, | |
77 | }; | |
78 | ||
79 | static error_t parse(int key, char* arg, struct argp_state* state, void* data) { | |
80 | struct cli_local_args* args = data; | |
81 | ||
82 | switch (key) { | |
83 | case OPT_DISABLE_CCACHE: | |
84 | args->flags &= ~BUILD_ENABLE_CCACHE; | |
85 | break; | |
86 | ||
87 | case OPT_DISABLE_SNAPSHOT: | |
88 | args->flags &= ~BUILD_ENABLE_SNAPSHOT; | |
89 | break; | |
90 | ||
91 | case OPT_DISABLE_TESTS: | |
92 | args->flags &= ~BUILD_ENABLE_TESTS; | |
93 | break; | |
94 | ||
95 | case OPT_LOG_FILE: | |
96 | args->log_path = arg; | |
97 | break; | |
98 | ||
99 | case OPT_NON_INTERACTIVE: | |
100 | args->flags &= ~BUILD_INTERACTIVE; | |
101 | break; | |
102 | ||
103 | case OPT_TARGET: | |
104 | args->target = arg; | |
105 | break; | |
106 | ||
107 | case ARGP_KEY_ARG: | |
108 | if (args->num_makefiles >= MAX_MAKEFILES) | |
109 | return -ENOBUFS; | |
110 | ||
111 | args->makefiles[args->num_makefiles++] = arg; | |
112 | break; | |
113 | ||
114 | default: | |
115 | return ARGP_ERR_UNKNOWN; | |
116 | } | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | static void log_callback(void* data, int priority, const char* file, int line, | |
122 | const char* function, const char* format, va_list args) | |
123 | __attribute__((format(printf, 6, 0))); | |
124 | ||
125 | static void log_callback(void* data, int priority, const char* file, int line, | |
126 | const char* function, const char* format, va_list args) { | |
127 | const struct cli_local_args* local_args = data; | |
128 | char* buffer = NULL; | |
129 | ssize_t length; | |
130 | ||
131 | // Format the line | |
132 | length = vasprintf(&buffer, format, args); | |
133 | if (length < 0) | |
134 | return; | |
135 | ||
136 | switch (priority) { | |
137 | // Highlight any warnings | |
138 | case LOG_WARNING: | |
139 | fputs(color_yellow(), stderr); | |
140 | fprintf(stderr, "%s", buffer); | |
141 | fputs(color_reset(), stderr); | |
142 | break; | |
143 | ||
144 | // Highlight any error messages | |
145 | case LOG_ERR: | |
146 | fputs(color_highlight(), stderr); | |
147 | fprintf(stderr, "%s", buffer); | |
148 | fputs(color_reset(), stderr); | |
149 | break; | |
150 | ||
151 | // Print anything to standard output | |
152 | default: | |
153 | fprintf(stdout, "%s", buffer); | |
154 | break; | |
155 | } | |
156 | ||
157 | // Write to the log file | |
158 | if (local_args->log_file) | |
159 | pakfire_log_file_write(local_args->log_file, priority, buffer, length); | |
160 | ||
161 | if (buffer) | |
162 | free(buffer); | |
163 | } | |
164 | ||
165 | static int result_callback(pakfire_ctx* ctx, pakfire_root* root, | |
166 | pakfire_build* build, pakfire_archive* archive, void* data) { | |
167 | const struct cli_local_args* local_args = data; | |
168 | pakfire_package* pkg = NULL; | |
169 | pakfire_repo* local = NULL; | |
170 | char path[PATH_MAX]; | |
171 | int r; | |
172 | ||
173 | // Fetch the package metadata | |
174 | r = pakfire_archive_make_package(archive, NULL, &pkg); | |
175 | if (r < 0) | |
176 | goto ERROR; | |
177 | ||
178 | // Fetch NEVRA | |
179 | const char* nevra = pakfire_package_get_string(pkg, PAKFIRE_PKG_NEVRA); | |
180 | ||
181 | // Fetch the local repository & import the archive | |
182 | local = pakfire_root_get_repo(root, PAKFIRE_REPO_LOCAL); | |
183 | if (local) { | |
184 | r = pakfire_repo_import_archive(local, archive, NULL); | |
185 | if (r < 0) { | |
186 | ERROR(ctx, "Could not import %s to the local repository: %s\n", | |
187 | nevra, strerror(-r)); | |
188 | goto ERROR; | |
189 | } | |
190 | } | |
191 | ||
192 | // Copy to the target (if given) | |
193 | if (local_args->target) { | |
194 | // Fetch the filename | |
195 | const char* filename = pakfire_package_get_filename(pkg); | |
196 | ||
197 | // Make the absolute target path | |
198 | r = pakfire_path_append(path, local_args->target, filename); | |
199 | if (r < 0) | |
200 | goto ERROR; | |
201 | ||
202 | // Copy (or link) the package to the target path | |
203 | r = pakfire_archive_link_or_copy(archive, path); | |
204 | if (r < 0) { | |
205 | ERROR(ctx, "Could not copy %s to %s: %s\n", | |
206 | nevra, path, strerror(-r)); | |
207 | goto ERROR; | |
208 | } | |
209 | } | |
210 | ||
211 | ERROR: | |
212 | if (local) | |
213 | pakfire_repo_unref(local); | |
214 | if (pkg) | |
215 | pakfire_package_unref(pkg); | |
216 | ||
217 | return r; | |
218 | } | |
219 | ||
220 | int cli_build(void* data, int argc, char* argv[]) { | |
221 | struct cli_global_args* global_args = data; | |
222 | struct cli_local_args local_args = { | |
223 | .flags = | |
224 | BUILD_INTERACTIVE | | |
225 | BUILD_ENABLE_CCACHE | | |
226 | BUILD_ENABLE_SNAPSHOT | | |
227 | BUILD_ENABLE_TESTS, | |
228 | }; | |
229 | pakfire_build* build = NULL; | |
230 | int flags = PAKFIRE_BUILD_LOCAL; | |
231 | int r; | |
232 | ||
233 | // Parse the command line | |
234 | r = cli_parse(options, NULL, NULL, NULL, parse, 0, argc, argv, &local_args); | |
235 | if (r) | |
236 | goto ERROR; | |
237 | ||
238 | // Replace the logger | |
239 | pakfire_ctx_set_log_callback(global_args->ctx, log_callback, &local_args); | |
240 | ||
241 | // Use the snapshot? | |
242 | if (local_args.flags & BUILD_ENABLE_SNAPSHOT) | |
243 | flags |= PAKFIRE_BUILD_ENABLE_SNAPSHOT; | |
244 | ||
245 | // Is the build interactive? | |
246 | if (local_args.flags & BUILD_INTERACTIVE) | |
247 | flags |= PAKFIRE_BUILD_INTERACTIVE; | |
248 | ||
249 | // Enable ccache? | |
250 | if (!(local_args.flags & BUILD_ENABLE_CCACHE)) | |
251 | flags |= PAKFIRE_BUILD_DISABLE_CCACHE; | |
252 | ||
253 | // Enable tests? | |
254 | if (!(local_args.flags & BUILD_ENABLE_TESTS)) | |
255 | flags |= PAKFIRE_BUILD_DISABLE_TESTS; | |
256 | ||
257 | // Create the log file | |
258 | if (local_args.log_path) { | |
259 | r = pakfire_log_file_create(&local_args.log_file, | |
260 | global_args->ctx, local_args.log_path, NULL, 0); | |
261 | if (r < 0) | |
262 | goto ERROR; | |
263 | } | |
264 | ||
265 | // Setup the build environment | |
266 | r = cli_setup_build(&build, global_args, flags); | |
267 | if (r < 0) | |
268 | goto ERROR; | |
269 | ||
270 | // Process all packages | |
271 | for (unsigned int i = 0; i < local_args.num_makefiles; i++) { | |
272 | // Run the build | |
273 | r = pakfire_build_exec(build, local_args.makefiles[i], result_callback, &local_args); | |
274 | if (r) { | |
275 | fprintf(stderr, "Could not build %s\n", local_args.makefiles[i]); | |
276 | goto ERROR; | |
277 | } | |
278 | } | |
279 | ||
280 | ERROR: | |
281 | if (local_args.log_file) | |
282 | pakfire_log_file_unref(local_args.log_file); | |
283 | if (build) | |
284 | pakfire_build_unref(build); | |
285 | ||
286 | return r; | |
287 | } |