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