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