]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
scripts: Add check-buildroot
[people/stevee/pakfire.git] / src / libpakfire / build.c
CommitLineData
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
36static 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 54static 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
85ERROR:
86 if (script)
87 free(script);
88
89 return r;
90}
91
73543ae3
MT
92static 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
113static 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
179ERROR:
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
197static 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
259ERROR:
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 272static 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
301ERROR:
302 if (packages)
303 free(packages);
304
305 return r;
306}
307
7d999600
MT
308static 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
333ERROR:
334 if (script)
335 free(script);
336
337 return r;
338}
339
39f45b30
MT
340static const char* post_build_scripts[] = {
341 "remove-static-libs",
99aee237 342 "check-buildroot",
39f45b30 343 "compress-man-pages",
ffb65de6 344 "strip",
39f45b30
MT
345 NULL,
346};
347
348static int pakfire_build_run_post_build_scripts(Pakfire pakfire, const char* buildroot,
349 pakfire_execute_logging_callback logging_callback, void* data) {
350 // Set default arguments for build scripts
351 const char* args[] = {
352 buildroot, NULL
353 };
354
355 // Run them one by one
356 for (const char** script = post_build_scripts; *script; script++) {
357 int r = pakfire_build_run_script(pakfire, *script, args, logging_callback, data);
358 if (r)
359 return r;
360 }
361
362 return 0;
363}
364
1a276007 365PAKFIRE_EXPORT int pakfire_build(Pakfire pakfire, const char* path,
7d999600
MT
366 const char* target, int flags,
367 pakfire_execute_logging_callback logging_callback, void* data) {
1a276007 368 PakfireParser makefile = NULL;
3d4011eb 369 char buildroot[PATH_MAX];
1a276007 370 struct pakfire_parser_error* error = NULL;
3d4011eb 371 int r;
1a276007 372
48c6f2e7
MT
373 // Check for valid input
374 if (!path) {
375 errno = EINVAL;
376 return 1;
377 }
378
379 // The default target is the local repository path
380 if (!target) {
381 PakfireRepo repo = pakfire_get_repo(pakfire, "local");
382 if (!repo) {
383 errno = EINVAL;
384 return 1;
385 }
386
387 target = pakfire_repo_get_path(repo);
388 pakfire_repo_unref(repo);
389 }
390
3d4011eb
MT
391 const char* root = pakfire_get_path(pakfire);
392
393 // Create BUILDROOT
394 pakfire_make_path(pakfire, buildroot, "/tmp/.buildroot.XXXXXX");
395 if (!pakfire_mkdtemp(buildroot))
396 return 1;
397
398 // Make relative BUILDROOT path
399 const char* buildroot_rel = pakfire_path_relpath(root, buildroot);
400 if (!buildroot_rel)
401 return 1;
402
1a276007 403 // Read makefile
3d4011eb 404 r = pakfire_read_makefile(&makefile, pakfire, path, &error);
1a276007
MT
405 if (r) {
406 if (error) {
407 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
408 pakfire_parser_error_get_message(error));
409 pakfire_parser_error_unref(error);
410 } else {
411 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
412 strerror(errno));
413 }
414
415 goto ERROR;
416 }
417
3d4011eb
MT
418 // Set BUILDROOT
419 pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel);
420
1a276007
MT
421 // Run through all build stages
422 for (const char** stage = stages; *stage; stage++) {
7d999600 423 int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data);
1a276007
MT
424 if (r) {
425 // Drop to a shell for debugging
426 if (flags & PAKFIRE_BUILD_INTERACTIVE)
427 pakfire_shell(pakfire);
428
429 goto ERROR;
430 }
431 }
432
39f45b30
MT
433 // Run post build scripts
434 r = pakfire_build_run_post_build_scripts(pakfire, buildroot_rel, logging_callback, data);
8d0f3a35
MT
435 if (r)
436 goto ERROR;
437
a50bde9c 438 // Create the packages
48c6f2e7 439 r = pakfire_build_packages(pakfire, makefile, target);
a50bde9c
MT
440 if (r) {
441 ERROR(pakfire, "Could not create packages: %s\n", strerror(errno));
442 goto ERROR;
443 }
1a276007
MT
444
445ERROR:
446 if (makefile)
447 pakfire_parser_unref(makefile);
448
3d4011eb
MT
449 // Remove buildroot
450 pakfire_rmtree(buildroot, 0);
451
1a276007
MT
452 return r;
453}
454
455PAKFIRE_EXPORT int pakfire_shell(Pakfire pakfire) {
456 const char* argv[] = {
457 "/bin/bash", "--login", NULL,
458 };
459
460 const int flags =
461 PAKFIRE_EXECUTE_INTERACTIVE | PAKFIRE_EXECUTE_ENABLE_NETWORK;
462
463 return pakfire_execute(pakfire, argv, NULL, flags, NULL, NULL);
464}