]>
Commit | Line | Data |
---|---|---|
1a276007 MT |
1 | /*############################################################################# |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2021 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 <errno.h> | |
22 | #include <stdlib.h> | |
23 | ||
24 | #include <pakfire/build.h> | |
25 | #include <pakfire/execute.h> | |
26 | #include <pakfire/dist.h> | |
ae7968a7 | 27 | #include <pakfire/file.h> |
1a276007 | 28 | #include <pakfire/logging.h> |
4c07774f | 29 | #include <pakfire/package.h> |
48c6f2e7 | 30 | #include <pakfire/packager.h> |
1a276007 MT |
31 | #include <pakfire/parser.h> |
32 | #include <pakfire/private.h> | |
33 | #include <pakfire/types.h> | |
34 | #include <pakfire/util.h> | |
35 | ||
36 | static const char* stages[] = { | |
37 | "prepare", | |
38 | "build", | |
39 | "test", | |
40 | "install", | |
41 | NULL, | |
42 | }; | |
43 | ||
44 | #define TEMPLATE \ | |
45 | "#!/bin/bash --login\n" \ | |
46 | "\n" \ | |
47 | "set -e\n" \ | |
48 | "set -x\n" \ | |
49 | "\n" \ | |
50 | "%%{_%s}\n" \ | |
51 | "\n" \ | |
52 | "exit 0\n" | |
53 | ||
29f2151d | 54 | static int pakfire_build_run_script(Pakfire pakfire, const char* filename, const char* args[], |
8d0f3a35 MT |
55 | pakfire_execute_logging_callback logging_callback, void* data) { |
56 | char* script = NULL; | |
57 | size_t size = 0; | |
58 | char path[PATH_MAX]; | |
59 | ||
60 | DEBUG(pakfire, "Running build script '%s'...\n", filename); | |
61 | ||
62 | // Make the source path | |
63 | pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename); | |
64 | ||
65 | // Open the source script | |
66 | FILE* f = fopen(path, "r"); | |
67 | if (!f) { | |
68 | ERROR(pakfire, "Could not open %s: %s\n", path, strerror(errno)); | |
69 | return 1; | |
70 | } | |
71 | ||
72 | // Read the script into memory | |
73 | int r = pakfire_read_file_into_buffer(f, &script, &size); | |
74 | if (r) { | |
75 | ERROR(pakfire, "Could not read script into memory: %s\n", strerror(errno)); | |
76 | goto ERROR; | |
77 | } | |
78 | ||
79 | // Execute it | |
29f2151d | 80 | r = pakfire_execute_script(pakfire, script, size, args, NULL, 0, logging_callback, data); |
8d0f3a35 MT |
81 | if (r) { |
82 | ERROR(pakfire, "Script '%s' failed with status %d\n", filename, r); | |
83 | } | |
84 | ||
85 | ERROR: | |
86 | if (script) | |
87 | free(script); | |
88 | ||
89 | return r; | |
90 | } | |
91 | ||
73543ae3 MT |
92 | static int append_to_array(const char*** array, const char* s) { |
93 | unsigned int length = 0; | |
94 | ||
95 | // Determine the length of the existing array | |
96 | if (*array) { | |
97 | for (const char** element = *array; *element; element++) | |
98 | length++; | |
99 | } | |
100 | ||
101 | // Allocate space | |
102 | *array = reallocarray(*array, length + 2, sizeof(**array)); | |
103 | if (!*array) | |
104 | return 1; | |
105 | ||
106 | // Append s and terminate the array | |
107 | (*array)[length] = s; | |
108 | (*array)[length + 1] = NULL; | |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
113 | static int pakfire_build_package_add_files(Pakfire pakfire, PakfireParser makefile, | |
114 | const char* namespace, struct pakfire_packager* packager) { | |
115 | PakfireFilelist filelist = NULL; | |
116 | char path[PATH_MAX]; | |
117 | int r = 1; | |
118 | ||
119 | char** files = NULL; | |
120 | const char** includes = NULL; | |
121 | const char** excludes = NULL; | |
122 | ||
123 | // Fetch filelist from makefile | |
124 | files = pakfire_parser_get_split(makefile, namespace, "files", '\n'); | |
125 | ||
126 | // No files to package? | |
127 | if (!files) | |
128 | return 0; | |
129 | ||
130 | // Fetch buildroot | |
131 | char* buildroot = pakfire_parser_get(makefile, NULL, "BUILDROOT"); | |
132 | if (!buildroot) | |
133 | goto ERROR; | |
134 | ||
135 | // Convert to absolute path | |
136 | pakfire_make_path(pakfire, path, buildroot); | |
137 | ||
138 | // Split into includes and excludes | |
139 | for (char** file = files; *file; file++) { | |
140 | if (**file == '!') | |
141 | r = append_to_array(&excludes, *file); | |
142 | else | |
143 | r = append_to_array(&includes, *file); | |
144 | if (r) | |
145 | goto ERROR; | |
146 | } | |
147 | ||
148 | // Allocate a new filelist | |
149 | r = pakfire_filelist_create(&filelist, pakfire); | |
150 | if (r) | |
151 | goto ERROR; | |
152 | ||
153 | // Scan for files | |
154 | r = pakfire_filelist_scan(filelist, path, includes, excludes); | |
155 | if (r) | |
156 | goto ERROR; | |
157 | ||
ae7968a7 | 158 | const size_t length = pakfire_filelist_size(filelist); |
73543ae3 MT |
159 | DEBUG(pakfire, "%zu file(s) found\n", length); |
160 | ||
ae7968a7 MT |
161 | // Add all files to the package |
162 | for (unsigned int i = 0; i < length; i++) { | |
163 | PakfireFile file = pakfire_filelist_get(filelist, i); | |
164 | ||
165 | // Add the file to the package | |
166 | r = pakfire_packager_add( | |
167 | packager, | |
168 | pakfire_file_get_abspath(file), | |
169 | pakfire_file_get_path(file) | |
170 | ); | |
171 | if (r) { | |
172 | pakfire_file_unref(file); | |
173 | goto ERROR; | |
174 | } | |
175 | ||
176 | pakfire_file_unref(file); | |
177 | } | |
73543ae3 MT |
178 | |
179 | ERROR: | |
180 | if (filelist) | |
181 | pakfire_filelist_unref(filelist); | |
182 | if (files) { | |
183 | for (char** file = files; *file; file++) | |
184 | free(*file); | |
185 | free(files); | |
186 | } | |
187 | if (includes) | |
188 | free(includes); | |
189 | if (excludes) | |
190 | free(excludes); | |
191 | if (buildroot) | |
192 | free(buildroot); | |
193 | ||
194 | return r; | |
195 | } | |
196 | ||
48c6f2e7 MT |
197 | static int pakfire_build_package(Pakfire pakfire, PakfireParser makefile, |
198 | const char* namespace, const char* target) { | |
4c07774f MT |
199 | PakfireRepo repo = NULL; |
200 | PakfirePackage pkg = NULL; | |
48c6f2e7 | 201 | struct pakfire_packager* packager = NULL; |
73543ae3 | 202 | |
a50bde9c MT |
203 | int r = 1; |
204 | ||
205 | // Expand the handle into the package name | |
4c07774f | 206 | char* name = pakfire_parser_expand(makefile, "packages", namespace); |
a50bde9c MT |
207 | if (!name) { |
208 | ERROR(pakfire, "Could not get package name: %s\n", strerror(errno)); | |
209 | goto ERROR; | |
210 | } | |
211 | ||
212 | INFO(pakfire, "Building package '%s'...\n", name); | |
213 | ||
4c07774f MT |
214 | // Fetch build architecture |
215 | const char* arch = pakfire_get_arch(pakfire); | |
216 | if (!arch) | |
217 | goto ERROR; | |
218 | ||
219 | // Fetch the dummy repository | |
220 | repo = pakfire_get_repo(pakfire, "@dummy"); | |
221 | if (!repo) | |
222 | goto ERROR; | |
223 | ||
224 | // Fetch package from makefile | |
225 | r = pakfire_parser_create_package(makefile, &pkg, repo, namespace, arch); | |
226 | if (r) { | |
227 | ERROR(pakfire, "Could not create package from makefile: %s\n", strerror(errno)); | |
228 | goto ERROR; | |
229 | } | |
230 | ||
48c6f2e7 MT |
231 | // Create a packager |
232 | r = pakfire_packager_create(&packager, pkg); | |
233 | if (r) | |
234 | goto ERROR; | |
235 | ||
73543ae3 MT |
236 | // Add files |
237 | r = pakfire_build_package_add_files(pakfire, makefile, namespace, packager); | |
238 | if (r) | |
239 | goto ERROR; | |
240 | ||
ae7968a7 MT |
241 | // Write the finished package |
242 | r = pakfire_packager_finish_to_directory(packager, target); | |
243 | if (r) { | |
244 | ERROR(pakfire, "pakfire_packager_finish() failed: %s\n", strerror(errno)); | |
245 | goto ERROR; | |
246 | } | |
247 | ||
4c07774f | 248 | #ifdef ENABLE_DEBUG |
23e22751 | 249 | char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG); |
4c07774f MT |
250 | if (dump) { |
251 | DEBUG(pakfire, "%s\n", dump); | |
252 | free(dump); | |
253 | } | |
254 | #endif | |
a50bde9c MT |
255 | |
256 | // Success | |
257 | r = 0; | |
258 | ||
259 | ERROR: | |
48c6f2e7 MT |
260 | if (packager) |
261 | pakfire_packager_unref(packager); | |
4c07774f MT |
262 | if (repo) |
263 | pakfire_repo_unref(repo); | |
264 | if (pkg) | |
265 | pakfire_package_unref(pkg); | |
a50bde9c MT |
266 | if (name) |
267 | free(name); | |
268 | ||
269 | return r; | |
270 | } | |
271 | ||
48c6f2e7 | 272 | static int pakfire_build_packages(Pakfire pakfire, PakfireParser makefile, const char* target) { |
a50bde9c MT |
273 | DEBUG(pakfire, "Creating packages..."); |
274 | int r = 1; | |
275 | ||
276 | // Fetch a list all all packages | |
277 | char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*"); | |
278 | if (!packages) { | |
279 | ERROR(pakfire, "Could not find any packages: %s\n", strerror(errno)); | |
280 | goto ERROR; | |
281 | } | |
282 | ||
283 | unsigned int num_packages = 0; | |
284 | ||
285 | // Count how many packages we have | |
286 | for (char** package = packages; *package; package++) | |
287 | num_packages++; | |
288 | ||
289 | DEBUG(pakfire, "Found %d package(s)\n", num_packages); | |
290 | ||
291 | // Build packages in reverse order | |
292 | for (int i = num_packages - 1; i >= 0; i--) { | |
48c6f2e7 | 293 | r = pakfire_build_package(pakfire, makefile, packages[i], target); |
a50bde9c MT |
294 | if (r) |
295 | goto ERROR; | |
296 | } | |
297 | ||
298 | // Success | |
299 | r = 0; | |
300 | ||
301 | ERROR: | |
302 | if (packages) | |
303 | free(packages); | |
304 | ||
305 | return r; | |
306 | } | |
307 | ||
7d999600 MT |
308 | static int pakfire_build_stage(Pakfire pakfire, PakfireParser makefile, const char* stage, |
309 | pakfire_execute_logging_callback logging_callback, void* data) { | |
1a276007 MT |
310 | char template[1024]; |
311 | ||
312 | // Prepare template for this stage | |
313 | int r = pakfire_string_format(template, TEMPLATE, stage); | |
314 | if (r < 0) | |
315 | return r; | |
316 | ||
317 | // Create the build script | |
318 | char* script = pakfire_parser_expand(makefile, "build", template); | |
319 | if (!script) { | |
320 | ERROR(pakfire, "Could not generate the build script for stage '%s': %s\n", | |
321 | stage, strerror(errno)); | |
322 | goto ERROR; | |
323 | } | |
324 | ||
325 | INFO(pakfire, "Running build stage '%s'\n", stage); | |
326 | ||
29f2151d | 327 | r = pakfire_execute_script(pakfire, script, strlen(script), NULL, NULL, 0, |
7d999600 | 328 | logging_callback, data); |
1a276007 MT |
329 | if (r) { |
330 | ERROR(pakfire, "Build stage '%s' failed with status %d\n", stage, r); | |
331 | } | |
332 | ||
333 | ERROR: | |
334 | if (script) | |
335 | free(script); | |
336 | ||
337 | return r; | |
338 | } | |
339 | ||
39f45b30 MT |
340 | static const char* post_build_scripts[] = { |
341 | "remove-static-libs", | |
1f62ffae | 342 | "check-symlinks", |
022b794e | 343 | "check-unsafe-files", |
99aee237 | 344 | "check-buildroot", |
7e1fec6f | 345 | "check-include", |
dd864160 | 346 | "check-hardening", |
39f45b30 | 347 | "compress-man-pages", |
ffb65de6 | 348 | "strip", |
39f45b30 MT |
349 | NULL, |
350 | }; | |
351 | ||
352 | static int pakfire_build_run_post_build_scripts(Pakfire pakfire, const char* buildroot, | |
353 | pakfire_execute_logging_callback logging_callback, void* data) { | |
354 | // Set default arguments for build scripts | |
355 | const char* args[] = { | |
356 | buildroot, NULL | |
357 | }; | |
358 | ||
359 | // Run them one by one | |
360 | for (const char** script = post_build_scripts; *script; script++) { | |
361 | int r = pakfire_build_run_script(pakfire, *script, args, logging_callback, data); | |
362 | if (r) | |
363 | return r; | |
364 | } | |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
1a276007 | 369 | PAKFIRE_EXPORT int pakfire_build(Pakfire pakfire, const char* path, |
7d999600 MT |
370 | const char* target, int flags, |
371 | pakfire_execute_logging_callback logging_callback, void* data) { | |
1a276007 | 372 | PakfireParser makefile = NULL; |
3d4011eb | 373 | char buildroot[PATH_MAX]; |
1a276007 | 374 | struct pakfire_parser_error* error = NULL; |
3d4011eb | 375 | int r; |
1a276007 | 376 | |
48c6f2e7 MT |
377 | // Check for valid input |
378 | if (!path) { | |
379 | errno = EINVAL; | |
380 | return 1; | |
381 | } | |
382 | ||
383 | // The default target is the local repository path | |
384 | if (!target) { | |
385 | PakfireRepo repo = pakfire_get_repo(pakfire, "local"); | |
386 | if (!repo) { | |
387 | errno = EINVAL; | |
388 | return 1; | |
389 | } | |
390 | ||
391 | target = pakfire_repo_get_path(repo); | |
392 | pakfire_repo_unref(repo); | |
393 | } | |
394 | ||
3d4011eb MT |
395 | const char* root = pakfire_get_path(pakfire); |
396 | ||
397 | // Create BUILDROOT | |
398 | pakfire_make_path(pakfire, buildroot, "/tmp/.buildroot.XXXXXX"); | |
399 | if (!pakfire_mkdtemp(buildroot)) | |
400 | return 1; | |
401 | ||
402 | // Make relative BUILDROOT path | |
403 | const char* buildroot_rel = pakfire_path_relpath(root, buildroot); | |
404 | if (!buildroot_rel) | |
405 | return 1; | |
406 | ||
1a276007 | 407 | // Read makefile |
3d4011eb | 408 | r = pakfire_read_makefile(&makefile, pakfire, path, &error); |
1a276007 MT |
409 | if (r) { |
410 | if (error) { | |
411 | ERROR(pakfire, "Could not parse makefile %s: %s\n", path, | |
412 | pakfire_parser_error_get_message(error)); | |
413 | pakfire_parser_error_unref(error); | |
414 | } else { | |
415 | ERROR(pakfire, "Could not parse makefile %s: %s\n", path, | |
416 | strerror(errno)); | |
417 | } | |
418 | ||
419 | goto ERROR; | |
420 | } | |
421 | ||
3d4011eb MT |
422 | // Set BUILDROOT |
423 | pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel); | |
424 | ||
1a276007 MT |
425 | // Run through all build stages |
426 | for (const char** stage = stages; *stage; stage++) { | |
7d999600 | 427 | int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data); |
1a276007 MT |
428 | if (r) { |
429 | // Drop to a shell for debugging | |
430 | if (flags & PAKFIRE_BUILD_INTERACTIVE) | |
431 | pakfire_shell(pakfire); | |
432 | ||
433 | goto ERROR; | |
434 | } | |
435 | } | |
436 | ||
39f45b30 MT |
437 | // Run post build scripts |
438 | r = pakfire_build_run_post_build_scripts(pakfire, buildroot_rel, logging_callback, data); | |
8d0f3a35 MT |
439 | if (r) |
440 | goto ERROR; | |
441 | ||
a50bde9c | 442 | // Create the packages |
48c6f2e7 | 443 | r = pakfire_build_packages(pakfire, makefile, target); |
a50bde9c MT |
444 | if (r) { |
445 | ERROR(pakfire, "Could not create packages: %s\n", strerror(errno)); | |
446 | goto ERROR; | |
447 | } | |
1a276007 MT |
448 | |
449 | ERROR: | |
450 | if (makefile) | |
451 | pakfire_parser_unref(makefile); | |
452 | ||
3d4011eb MT |
453 | // Remove buildroot |
454 | pakfire_rmtree(buildroot, 0); | |
455 | ||
1a276007 MT |
456 | return r; |
457 | } | |
458 | ||
459 | PAKFIRE_EXPORT int pakfire_shell(Pakfire pakfire) { | |
460 | const char* argv[] = { | |
461 | "/bin/bash", "--login", NULL, | |
462 | }; | |
463 | ||
464 | const int flags = | |
465 | PAKFIRE_EXECUTE_INTERACTIVE | PAKFIRE_EXECUTE_ENABLE_NETWORK; | |
466 | ||
467 | return pakfire_execute(pakfire, argv, NULL, flags, NULL, NULL); | |
468 | } |