1 //
2 // aegis - project change supervisor
3 // Copyright (C) 2007-2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or (at
8 // your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18
19 #include <common/ac/assert.h>
20 #include <common/ac/string.h>
21 #include <libaegis/output/filter/wrap_make.h>
22
23 #include <common/trace.h>
24
25 #include <aemakegen/target/make/automake.h>
26 #include <aemakegen/util.h>
27
28
~target_make_automake()29 target_make_automake::~target_make_automake()
30 {
31 trace(("%s\n", __PRETTY_FUNCTION__));
32 }
33
34
target_make_automake(change_identifier & a_cid)35 target_make_automake::target_make_automake(change_identifier &a_cid) :
36 target_make(a_cid)
37 {
38 trace(("%s\n", __PRETTY_FUNCTION__));
39 op = output_filter_wrap_make::create(op);
40 }
41
42
43 target::pointer
create(change_identifier & a_cid)44 target_make_automake::create(change_identifier &a_cid)
45 {
46 trace(("%s\n", __PRETTY_FUNCTION__));
47 return pointer(new target_make_automake(a_cid));
48 }
49
50
51 void
process3_begin(void)52 target_make_automake::process3_begin(void)
53 {
54 trace(("%s\n", __PRETTY_FUNCTION__));
55 warning_this_file_is_generated();
56
57 print_comment
58 (
59 "\n"
60 "Tell automake to put the object file for foo/bar.cc in "
61 "directory foo/\n"
62 "\n"
63 );
64 print_assignment("AUTOMAKE_OPTIONS", "subdir-objects");
65 }
66
67
68 void
process_item_uudecode(const nstring & fn)69 target_make_automake::process_item_uudecode(const nstring &fn)
70 {
71 trace(("%s\n", __PRETTY_FUNCTION__));
72 nstring fn2 = nstring(fn.c_str(), fn.size() - 4);
73
74 // this isn't automatic in automake (?)
75 nstring_list lhs;
76 lhs.push_back(fn2);
77 nstring_list rhs;
78 rhs.push_back(fn);
79 nstring_list body;
80 body.push_back("uudecode -o " + fn2 + " " + fn);
81 location_comment(__FILE__, __LINE__);
82 print_rule(lhs, rhs, body);
83
84 // now process the result of the rule.
85 // it could be just about anything.
86 processing.run_process(fn2);
87 }
88
89
90 void
process_item_scripts(const nstring & fn)91 target_make_automake::process_item_scripts(const nstring &fn)
92 {
93 trace(("%s\n", __PRETTY_FUNCTION__));
94 //
95 // The logic here *must* match the logic in
96 // aemakegen/target/debian/process.cc
97 //
98 assert(filename_implies_is_a_script(fn));
99 if (fn.starts_with("test/"))
100 return;
101 nstring prog = fn.basename().trim_extension();
102 nstring bin_prog = "bin/" + prog;
103
104 nstring_list lhs;
105 lhs.push_back(bin_prog);
106 nstring_list rhs;
107 rhs.push_back(fn);
108 nstring_list body;
109 body.push_back("@mkdir -p " + bin_prog.dirname());
110 body.push_back("cp " + fn + " $@");
111 body.push_back("chmod a+rx $@");
112 location_comment(__FILE__, __LINE__);
113 print_rule(lhs, rhs, body);
114 }
115
116
117 void
process_item_aegis_lib_doc(const nstring & fn)118 target_make_automake::process_item_aegis_lib_doc(const nstring &fn)
119 {
120 trace(("%s\n", __PRETTY_FUNCTION__));
121 nstring macros = fn.get_extension();
122 if (macros == "roff")
123 macros = "";
124 else if (macros == "mm")
125 macros = "-m$(MM)";
126 else if (macros == "ms")
127 macros = "-m$(MS)";
128 else
129 {
130 if (!macros.starts_with("m"))
131 macros = "m" + macros;
132 macros = "-" + macros;
133 }
134 nstring src = fn.dirname() + ".pdf";
135 nstring ps = fn.dirname() + ".ps";
136
137 nstring_list lhs;
138 lhs.push_back(src);
139 nstring_list rhs;
140 rhs.push_back(fn);
141 rhs.push_back_unique(roff_include_dependencies(fn));
142 nstring_list body;
143 body.push_back
144 (
145 "$(GROFF) -Tps -I. -R -t -p " + macros + " -mpic -mpspic " + fn +
146 " > " + ps
147 );
148 body.push_back("ps2pdf " + ps + " $@");
149 body.push_back("rm " + ps);
150 location_comment(__FILE__, __LINE__);
151 print_rule(lhs, rhs, body);
152 }
153
154
155 void
process3_end(void)156 target_make_automake::process3_end(void)
157 {
158 trace(("%s\n", __PRETTY_FUNCTION__));
159 if (data.have_yacc() || data.need_yacc())
160 {
161 print_comment("\nWhich yacc flags to use.\n\n");
162 location_comment(__FILE__, __LINE__);
163 print_assignment("AM_YFLAGS", "-d");
164 }
165
166 if (data.seen_install_mandir())
167 {
168 print_comment("manual pages");
169 location_comment(__FILE__, __LINE__);
170 print_assignment_sorted("man_MANS", data.get_man_sources());
171 }
172
173 if (data.seen_all_bin())
174 {
175 nstring_list programs = data.get_all_bin();
176 programs.sort();
177 nstring_list bin_programs;
178 nstring_list noinst_programs;
179 nstring_list check_programs;
180 nstring_list bin_scripts;
181 nstring_list noinst_scripts;
182 nstring_list check_scripts;
183 for (size_t j = 0; j < programs.size(); ++j)
184 {
185 nstring bin_program = programs[j];
186 assert(bin_program.starts_with("bin/"));
187 nstring program = bin_program.substr(4, bin_program.size() - 4);
188 nstring progdir = data.progdir_from_progname(program);
189 bool is_a_script = data.get_object_files_by_dir(progdir).empty();
190 if (is_a_script)
191 {
192 if (data.is_explicit_noinst(program))
193 noinst_scripts.push_back(bin_program);
194 else
195 bin_scripts.push_back(bin_program);
196 if (program.starts_with("test"))
197 check_scripts.push_back(bin_program);
198 }
199 else
200 {
201 if (data.is_explicit_noinst(program))
202 noinst_programs.push_back(bin_program);
203 else
204 bin_programs.push_back(bin_program);
205 if (program.starts_with("test"))
206 check_programs.push_back(bin_program);
207 }
208 }
209
210 if (!bin_programs.empty())
211 {
212 print_comment("executables to be installed");
213 print_assignment_sorted("bin_PROGRAMS", bin_programs);
214 }
215 if (!noinst_programs.empty())
216 {
217 print_comment("executables not to be installed");
218 print_assignment_sorted("noinst_PROGRAMS", noinst_programs);
219 }
220 if (!check_programs.empty())
221 {
222 print_comment("executables needed to run tests");
223 print_assignment_sorted("check_PROGRAMS", check_programs);
224 }
225
226 if (!bin_scripts.empty())
227 {
228 print_comment("scripts to be installed");
229 print_assignment_sorted("bin_SCRIPTS", bin_scripts);
230 }
231 if (!noinst_scripts.empty())
232 {
233 print_comment("scripts not to be installed");
234 print_assignment_sorted("noinst_SCRIPTS", noinst_scripts);
235 }
236 if (!check_scripts.empty())
237 {
238 print_comment("scripts needed to run tests");
239 print_assignment_sorted("check_SCRIPTS", check_scripts);
240 }
241 }
242
243 if (data.seen_pkgconfig_source())
244 {
245 op->fputc('\n');
246 print_assignment("pkgconfigdir", "$(libdir)/pkgconfig");
247
248 print_comment("Data files to be installed in $(pkgconfigdir)");
249 print_assignment_sorted("pkgconfig_DATA", data.get_pkgconfig_sources());
250 }
251
252 // Emit the definitions for the library or libraries.
253 {
254 nstring_list lib_ltlibraries;
255 nstring_list noinst_libraries;
256 nstring_list lib_dirs = data.get_list_of_library_directories();
257 lib_dirs.sort();
258 for (size_t j = 0; j < lib_dirs.size(); ++j)
259 {
260 nstring library_dirname = lib_dirs[j];
261
262 nstring library_libname = library_dirname;
263 if (library_libname == "lib")
264 library_libname = get_project_name();
265 if (!library_libname.starts_with("lib"))
266 library_libname = "lib" + library_libname;
267
268 nstring path =
269 library_dirname + "/" + library_libname + "." + data.libext();
270
271 bool shared =
272 (
273 data.use_libtool()
274 &&
275 library_libname == get_library_libname()
276 );
277
278 print_comment("The " + path + " library.");
279
280 print_assignment_sorted
281 (
282 path.identifier() + "_includes",
283 data.get_include_files_by_dir(library_dirname)
284 );
285 print_assignment_sorted
286 (
287 path.identifier() + "_SOURCES",
288 data.get_source_files_by_dir(library_dirname)
289 );
290
291 if (shared)
292 {
293 print_assignment
294 (
295 path.identifier() + "_LDFLAGS",
296 "-version-info " + data.get_version_info()
297 );
298 lib_ltlibraries.push_back(path);
299 }
300 else
301 noinst_libraries.push_back(path);
302 }
303
304 if (!lib_ltlibraries.empty())
305 {
306 print_comment("Shared libraries, to be installed.");
307 print_assignment_sorted("lib_LTLIBRARIES", lib_ltlibraries);
308 }
309
310 if (!noinst_libraries.empty())
311 {
312 print_comment("Static libraries, not to be installed.");
313 print_assignment_sorted("noinst_LIBRARIES", noinst_libraries);
314 }
315 }
316
317 if (data.seen_datadir() || data.seen_datarootdir())
318 {
319 nstring_list files = data.get_install_datadir();
320 nstring_list pkgdatadir;
321 nstring_list datadir;
322 nstring_list datarootdir;
323 nstring projname = get_project_name() + "/";
324 for (size_t j = 0; j < files.size(); ++j)
325 {
326 nstring file = files[j];
327 if (file.starts_with("$(datarootdir)/"))
328 {
329 file = file.trim_first_directory();
330 datarootdir.push_back(file);
331 }
332 else if (file.starts_with("$(datadir)/"))
333 {
334 file = file.trim_first_directory();
335 if (file.starts_with(projname))
336 {
337 file = file.trim_first_directory();
338 pkgdatadir.push_back(file);
339 }
340 else
341 {
342 datadir.push_back(file);
343 }
344 }
345 else if (file.starts_with("$(sysconfdir)/"))
346 {
347 // FIXME: this is wrong, but I don't know how to force
348 // Automake to change the name of a file when it is
349 // installed.
350 datarootdir.push_back(file);
351 }
352 else
353 {
354 print_comment
355 (
356 "Don't know what to do with the " +
357 file.quote_c() + " file."
358 );
359 }
360 }
361 if (!pkgdatadir.empty())
362 {
363 print_comment
364 (
365 "Data files to be installed in $(datadir)/" + projname
366 );
367 location_comment(__FILE__, __LINE__);
368 print_assignment_sorted("dist_pkgdatadir_DATA", pkgdatadir);
369 }
370 if (!datadir.empty())
371 {
372 print_comment("Data files to be installed in $(datadir)");
373 location_comment(__FILE__, __LINE__);
374 print_assignment_sorted("dist_datadir_DATA", pkgdatadir);
375 }
376 if (!datarootdir.empty())
377 {
378 print_comment("Data files to be installed in $(datarootdir)");
379 location_comment(__FILE__, __LINE__);
380 print_assignment_sorted("dist_datarootdir_DATA", datarootdir);
381 }
382 }
383
384 {
385 nstring_list drd;
386 drd.push_back(data.get_am_data_data());
387 drd.push_back(data.get_install_i18n());
388 if (!drd.empty())
389 {
390 print_comment("Data to be installed below $(datarootdir)/");
391 location_comment(__FILE__, __LINE__);
392 print_assignment_sorted("data_DATA", drd);
393 }
394 }
395
396 if (!data.get_install_include_sources().empty())
397 {
398 print_comment("header files to be installed");
399 print_assignment_sorted
400 (
401 (
402 data.get_library_directory() == "lib"
403 ?
404 "include_HEADERS"
405 :
406 "nobase_include_HEADERS"
407 ),
408 data.get_install_include_sources()
409 );
410 }
411
412 if (data.seen_dist_clean_files())
413 {
414 print_comment("Files to be removed by the \"distclean\" make target.");
415 print_assignment_sorted("DISTCLEANFILES", data.get_dist_clean_files());
416 }
417
418 if (data.seen_programs())
419 {
420 bool package_library = !data.get_library_name().empty();
421 nstring_list programs = data.get_programs();
422 programs.sort();
423 for (size_t j = 0; j < programs.size(); ++j)
424 {
425 nstring prog = programs[j];
426 nstring iprog = prog.identifier();
427 print_comment("The " + prog + " program.");
428
429 print_assignment_sorted
430 (
431 "bin_" + iprog + "_includes",
432 data.get_include_files_by_dir(prog)
433 );
434 print_assignment_sorted
435 (
436 "bin_" + iprog + "_SOURCES",
437 data.get_source_files_by_dir(prog)
438 );
439
440 {
441 nstring_list ldadd = data.get_library_list_by_program(prog);
442 if (ldadd.empty() && package_library)
443 {
444 ldadd.push_back
445 (
446 data.get_library_directory() + "/" +
447 get_library_libname() + "." + data.libext()
448 );
449 }
450 print_assignment_sorted("bin_" + iprog + "_LDADD", ldadd);
451 }
452 }
453 }
454
455 if (data.seen_test_files())
456 {
457 //
458 // FIXME: this needs to be smarter about the test_command field
459 // of the aegis.conf project configuration file.
460 //
461 print_comment("How to run the test scripts.");
462 nstring_list rhs;
463 if (data.seen_etc_test_sh())
464 rhs.push_back("$(SHELL) etc/test.sh");
465 else
466 rhs.push_back("PATH=`pwd`/bin:$$PATH $(SHELL)");
467 print_assignment("TESTS_ENVIRONMENT", rhs);
468
469 print_comment("The test scripts to be run.");
470 print_assignment_sorted("TESTS", data.get_test_sources());
471 }
472
473 // These files don't appear in any other list of files,
474 // but are to be included in tarballs.
475 if (data.seen_extra_dist())
476 {
477 print_comment("Additional source files to be included in the tarball.");
478 print_assignment_sorted("EXTRA_DIST", data.get_extra_dist());
479 }
480
481 if (data.use_libtool())
482 {
483 //
484 // Libtool makes executables that have to be writable. Sheesh. This
485 // makes baseline testing and regression testing impossible, when
486 // you are using Aegis. So we have to execute thm once <i>at build
487 // time</i> to get all the crap resolved into an executable that
488 // <i>doesn't</i> need to be writable.
489 //
490 // (It doesn't matter if the program doesn't understand the -V option,
491 // it still has the effect of making the program the way we want it.)
492 //
493 op->fprintf("\n");
494 op->fprintf("all-am: goose-libtool-writable-crap\n");
495 op->fprintf("goose-libtool-writable-crap: $(PROGRAMS)\n");
496 nstring_list programs = data.get_programs();
497 programs.sort();
498 for (size_t j = 0; j < programs.size(); ++j)
499 {
500 nstring prog = programs[j];
501 op->fprintf("\t-bin/%s -V\n", prog.c_str());
502 }
503 }
504
505 //
506 // BUILT_SOURCES is how automake handles catch-22 include dependencies.
507 //
508 if (data.seen_built_sources())
509 {
510 print_comment("Catch-22 when building dependencies.");
511 location_comment(__FILE__, __LINE__);
512 print_assignment_sorted("BUILT_SOURCES", data.get_built_sources());
513 }
514
515 print_comment("vim: set ts=8 sw=8 noet :");
516 }
517
518
519 // vim: set ts=8 sw=4 et :
520