mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

jbsrc/lib/src/core/jb-resource.c (46793B) - raw

      1 /*
      2  * JB, the Jean-Yves Lefort's Build System
      3  * Copyright (C) 2008 Jean-Yves Lefort <jylefort@brutele.be>
      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
      8  * (at 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
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License along
     16  * with this program; if not, write to the Free Software Foundation, Inc.,
     17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     18  */
     19 
     20 #include <string.h>
     21 #include "jb-resource.h"
     22 #include "jb-group.h"
     23 #include "jb-variable.h"
     24 #include "jb-util.h"
     25 #include "jb-feature.h"
     26 #include "jb-action.h"
     27 
     28 #define INTLTOOL_MERGE_IN	"jbsrc/tools/intltool-merge.in"
     29 #define INTLTOOL_MERGE_OUT	"build/jbsrc/tools/intltool-merge"
     30 #define INTLTOOL_MERGE_CACHE	"build/jbsrc/tools/intltool-merge-cache"
     31 
     32 static gboolean
     33 has_template (const char *filename)
     34 {
     35   char *template_input;
     36   gboolean result;
     37 
     38   if (jb_templates == NULL)
     39     return FALSE;
     40 
     41   template_input = g_strdup_printf("%s.in", filename);
     42   result = jb_string_hash_set_contains(jb_templates, template_input);
     43   g_free(template_input);
     44 
     45   return result;
     46 }
     47 
     48 static char *
     49 get_input_file (const char *filename)
     50 {
     51   if (has_template(filename))
     52     return g_strdup_printf("build/%s", filename);
     53   else
     54     return g_strdup(filename);
     55 }
     56 
     57 static char *
     58 get_output_file (const char *filename)
     59 {
     60   char *tmp;
     61   char *result;
     62 
     63   g_return_val_if_fail(filename != NULL, NULL);
     64   g_return_val_if_fail(g_str_has_suffix(filename, ".in"), NULL);
     65 
     66   tmp = jb_strip_extension(filename);
     67 
     68   /*
     69    * If the input file is already in build/, do not put the output
     70    * file in build/build/.
     71    */
     72   if (g_str_has_prefix(tmp, "build/"))
     73     result = g_strdup(tmp);
     74   else
     75     result = g_strdup_printf("build/%s", tmp);
     76 
     77   g_free(tmp);
     78 
     79   return result;
     80 }
     81 
     82 G_DEFINE_ABSTRACT_TYPE(JBResource, jb_resource, G_TYPE_OBJECT)
     83 
     84 char *
     85 jb_resource_to_string (JBResource *self)
     86 {
     87   JBResourceClass *class;
     88 
     89   g_return_val_if_fail(JB_IS_RESOURCE(self), NULL);
     90 
     91   class = JB_RESOURCE_GET_CLASS(self);
     92 
     93   if (class->to_string != NULL)
     94     return class->to_string(self);
     95   else
     96     {
     97       g_error("to_string() not implemented for resource type %s", G_OBJECT_TYPE_NAME(self));
     98       return NULL;
     99     }
    100 }
    101 
    102 void
    103 jb_resource_pre_build (JBResource *self)
    104 {
    105   JBResourceClass *class;
    106 
    107   g_return_if_fail(JB_IS_RESOURCE(self));
    108 
    109   class = JB_RESOURCE_GET_CLASS(self);
    110 
    111   if (class->pre_build != NULL)
    112     class->pre_build(self);
    113 }
    114 
    115 void
    116 jb_resource_build (JBResource *self)
    117 {
    118   JBResourceClass *class;
    119 
    120   g_return_if_fail(JB_IS_RESOURCE(self));
    121 
    122   class = JB_RESOURCE_GET_CLASS(self);
    123 
    124   if (class->build != NULL)
    125     class->build(self);
    126 }
    127 
    128 void
    129 jb_resource_install (JBResource *self)
    130 {
    131   JBResourceClass *class;
    132 
    133   g_return_if_fail(JB_IS_RESOURCE(self));
    134 
    135   class = JB_RESOURCE_GET_CLASS(self);
    136 
    137   if (class->install != NULL)
    138     class->install(self);
    139 }
    140 
    141 void
    142 jb_resource_makedist (JBResource *self)
    143 {
    144   JBResourceClass *class;
    145 
    146   g_return_if_fail(JB_IS_RESOURCE(self));
    147 
    148   class = JB_RESOURCE_GET_CLASS(self);
    149 
    150   if (class->makedist != NULL)
    151     class->makedist(self);
    152 }
    153 
    154 void
    155 jb_resource_clean (JBResource *self)
    156 {
    157   JBResourceClass *class;
    158 
    159   g_return_if_fail(JB_IS_RESOURCE(self));
    160 
    161   class = JB_RESOURCE_GET_CLASS(self);
    162 
    163   if (class->clean != NULL)
    164     class->clean(self);
    165 }
    166 
    167 void
    168 jb_resource_distclean (JBResource *self)
    169 {
    170   JBResourceClass *class;
    171 
    172   g_return_if_fail(JB_IS_RESOURCE(self));
    173 
    174   class = JB_RESOURCE_GET_CLASS(self);
    175 
    176   if (class->distclean != NULL)
    177     class->distclean(self);
    178 }
    179 
    180 void
    181 jb_resource_maintainerclean (JBResource *self)
    182 {
    183   JBResourceClass *class;
    184 
    185   g_return_if_fail(JB_IS_RESOURCE(self));
    186 
    187   class = JB_RESOURCE_GET_CLASS(self);
    188 
    189   if (class->maintainerclean != NULL)
    190     class->maintainerclean(self);
    191 }
    192 
    193 static void
    194 resource_action_message (JBResource *self, const char *action)
    195 {
    196   char *repr;
    197 
    198   g_return_if_fail(JB_IS_RESOURCE(self));
    199   g_return_if_fail(action != NULL);
    200 
    201   repr = jb_resource_to_string(self);
    202   jb_message("%s %s", action, repr);
    203   g_free(repr);
    204 }
    205 
    206 void
    207 jb_resource_message_building (JBResource *self)
    208 {
    209   g_return_if_fail(JB_IS_RESOURCE(self));
    210 
    211   resource_action_message(self, "building");
    212 }
    213 
    214 static void
    215 jb_resource_init (JBResource *self)
    216 {
    217 }
    218 
    219 static void
    220 jb_resource_class_init (JBResourceClass *class)
    221 {
    222 }
    223 
    224 G_DEFINE_ABSTRACT_TYPE(JBGroupResource, jb_group_resource, JB_TYPE_RESOURCE)
    225 
    226 static void
    227 jb_group_resource_init (JBGroupResource *self)
    228 {
    229 }
    230 
    231 static void
    232 jb_group_resource_class_init (JBGroupResourceClass *class)
    233 {
    234 }
    235 
    236 G_DEFINE_ABSTRACT_TYPE(JBObjectResource, jb_object_resource, JB_TYPE_RESOURCE)
    237 
    238 static void
    239 jb_object_resource_init (JBObjectResource *self)
    240 {
    241 }
    242 
    243 static void
    244 jb_object_resource_class_init (JBObjectResourceClass *class)
    245 {
    246 }
    247 
    248 static char *
    249 object_resource_get_object_file (JBObjectResource *self)
    250 {
    251   JBObjectResourceClass *class;
    252 
    253   g_return_val_if_fail(JB_IS_OBJECT_RESOURCE(self), NULL);
    254 
    255   class = JB_OBJECT_RESOURCE_GET_CLASS(self);
    256 
    257   if (class->get_object_file != NULL)
    258     return class->get_object_file(self);
    259   else
    260     {
    261       g_error("get_object_file() not implemented for object resource type %s", G_OBJECT_TYPE_NAME(self));
    262       return NULL;
    263     }
    264 }
    265 
    266 G_DEFINE_TYPE(JBTemplate, jb_template, JB_TYPE_GROUP_RESOURCE)
    267 
    268 JBTemplate *
    269 jb_template_new (const char *filename)
    270 {
    271   JBTemplate *self;
    272 
    273   g_return_val_if_fail(filename != NULL, NULL);
    274   g_return_val_if_fail(g_str_has_suffix(filename, ".in"), NULL);
    275 
    276   self = g_object_new(JB_TYPE_TEMPLATE, NULL);
    277   self->filename = g_strdup(filename);
    278 
    279   return self;
    280 }
    281 
    282 static char *
    283 template_to_string (JBResource *res)
    284 {
    285   JBTemplate *self = JB_TEMPLATE(res);
    286 
    287   return g_strdup_printf("template %s", self->filename);
    288 }
    289 
    290 static void
    291 template_get_files (JBTemplate *self, char **infile, char **outfile)
    292 {
    293   JBGroupResource *gres = JB_GROUP_RESOURCE(self);
    294 
    295   if (infile != NULL)
    296     *infile = g_strdup_printf("%s/%s", gres->group->srcdir, self->filename);
    297   if (outfile != NULL)
    298     {
    299       char *tmp;
    300 
    301       tmp = jb_strip_extension(self->filename);
    302       *outfile = g_strdup_printf("%s/%s", gres->group->builddir, tmp);
    303       g_free(tmp);
    304     }
    305 }
    306 
    307 static void
    308 template_build (JBResource *res)
    309 {
    310   JBTemplate *self = JB_TEMPLATE(res);
    311   char *infile;
    312   char *outfile;
    313 
    314   template_get_files(self, &infile, &outfile);
    315 
    316   if (! jb_is_uptodate(outfile, infile))
    317     {
    318       static GHashTable *variables = NULL;
    319 
    320       jb_resource_message_building(res);
    321 
    322       if (variables == NULL)
    323 	{
    324 	  GSList *l;
    325 
    326 	  variables = g_hash_table_new(g_str_hash, g_str_equal);
    327 
    328 	  JB_LIST_FOREACH(l, jb_variables)
    329 	    {
    330 	      JBVariable *variable = l->data;
    331 
    332 	      g_hash_table_insert(variables, variable->name, jb_variable_evaluate(variable));
    333 	    }
    334 	}
    335 
    336       jb_mkdir_of_file(outfile);
    337 
    338       jb_subst(infile, outfile, variables);
    339     }
    340 
    341   g_free(infile);
    342   g_free(outfile);
    343 }
    344 
    345 static void
    346 template_makedist (JBResource *res)
    347 {
    348   JBTemplate *self = JB_TEMPLATE(res);
    349   char *infile;
    350 
    351   template_get_files(self, &infile, NULL);
    352 
    353   jb_action_add_to_dist(infile);
    354 
    355   g_free(infile);
    356 }
    357 
    358 static void
    359 template_clean (JBResource *res)
    360 {
    361   JBTemplate *self = JB_TEMPLATE(res);
    362   char *outfile;
    363 
    364   template_get_files(self, NULL, &outfile);
    365 
    366   jb_action_rm(outfile);
    367 
    368   g_free(outfile);
    369 }
    370 
    371 static void
    372 jb_template_init (JBTemplate *self)
    373 {
    374 }
    375 
    376 static void
    377 jb_template_class_init (JBTemplateClass *class)
    378 {
    379   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
    380 
    381   rclass->to_string = template_to_string;
    382   rclass->build = template_build;
    383   rclass->makedist = template_makedist;
    384   rclass->clean = template_clean;
    385 }
    386 
    387 G_DEFINE_TYPE(JBDataFile, jb_data_file, JB_TYPE_GROUP_RESOURCE)
    388 
    389 JBDataFile *
    390 jb_data_file_new (const char *filename)
    391 {
    392   JBDataFile *self;
    393 
    394   g_return_val_if_fail(filename != NULL, NULL);
    395 
    396   self = g_object_new(JB_TYPE_DATA_FILE, NULL);
    397   self->filename = g_strdup(filename);
    398 
    399   return self;
    400 }
    401 
    402 static char *
    403 data_file_to_string (JBResource *res)
    404 {
    405   JBDataFile *self = JB_DATA_FILE(res);
    406 
    407   return g_strdup_printf("data file %s", self->filename);
    408 }
    409 
    410 static void
    411 data_file_install (JBResource *res)
    412 {
    413   JBDataFile *self = JB_DATA_FILE(res);
    414   JBGroupResource *gres = JB_GROUP_RESOURCE(res);
    415   char *srcfile;
    416   char *real_srcfile;
    417   char *dstfile;
    418 
    419   srcfile = g_strdup_printf("%s/%s", gres->group->srcdir, self->filename);
    420   real_srcfile = get_input_file(srcfile);
    421   dstfile = g_path_get_basename(self->filename);
    422 
    423   jb_install_options_install_data(self->install_options, real_srcfile, dstfile);
    424 
    425   g_free(srcfile);
    426   g_free(real_srcfile);
    427   g_free(dstfile);
    428 }
    429 
    430 static void
    431 data_file_makedist (JBResource *res)
    432 {
    433   JBDataFile *self = JB_DATA_FILE(res);
    434   JBGroupResource *gres = JB_GROUP_RESOURCE(res);
    435   char *file;
    436 
    437   file = g_strdup_printf("%s/%s", gres->group->srcdir, self->filename);
    438 
    439   if (! has_template(file))
    440     jb_action_add_to_dist(file);
    441 
    442   g_free(file);
    443 }
    444 
    445 static void
    446 jb_data_file_init (JBDataFile *self)
    447 {
    448   self->install_options = jb_install_options_new();
    449 }
    450 
    451 static void
    452 jb_data_file_class_init (JBDataFileClass *class)
    453 {
    454   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
    455 
    456   rclass->to_string = data_file_to_string;
    457   rclass->install = data_file_install;
    458   rclass->makedist = data_file_makedist;
    459 }
    460 
    461 static GSList *
    462 parse_depsfile (const char *depsfile)
    463 {
    464   char *contents;
    465   char **lines;
    466   GSList *deps = NULL;
    467   int i;
    468 
    469   contents = jb_read_file(depsfile, NULL);
    470   if (contents == NULL)
    471     return NULL;		/* depsfile is optional */
    472 
    473   lines = g_strsplit(contents, "\n", 0);
    474   g_free(contents);
    475 
    476   for (i = 0; lines[i] != NULL; i++)
    477     {
    478       char *p;
    479       char **files;
    480       int j;
    481 
    482       p = strchr(lines[i], ':');
    483       if (p != NULL)
    484 	p++;
    485       else
    486 	p = lines[i];
    487 
    488       files = g_strsplit(p, " ", 0);
    489 
    490       for (j = 0; files[j] != NULL; j++)
    491 	{
    492 	  char *work;
    493 	  char *file;
    494 
    495 	  work = g_strdup(files[j]);
    496 	  file = g_strstrip(work);
    497 
    498 	  if (*file != '\0' && *file != '\\')
    499 	    deps = g_slist_prepend(deps, g_strdup(file));
    500 
    501 	  g_free(work);
    502 	}
    503 
    504       g_strfreev(files);
    505     }
    506 
    507   return deps;
    508 }
    509 
    510 static char *
    511 object_file_get_depsfile (const char *ofile)
    512 {
    513   return g_strdup_printf("%s.deps", ofile);
    514 }
    515 
    516 static gboolean
    517 object_file_is_uptodate (const char *ofile, const char *cfile)
    518 {
    519   char *depsfile;
    520   GSList *deps;
    521 
    522   if (! jb_variable_get_bool("cc-dependency-tracking"))
    523     return FALSE;
    524 
    525   if (! jb_is_uptodate(ofile, cfile))
    526     return FALSE;
    527 
    528   depsfile = object_file_get_depsfile(ofile);
    529   deps = parse_depsfile(depsfile);
    530   g_free(depsfile);
    531 
    532   if (deps != NULL)
    533     {
    534       gboolean is_uptodate;
    535 
    536       is_uptodate = jb_is_uptodate_list(ofile, deps);
    537 
    538       jb_g_slist_free_deep(deps);
    539 
    540       return is_uptodate;
    541     }
    542   else
    543     return TRUE;
    544 }
    545 
    546 static void
    547 object_file_build (JBObject *object, const char *ofile, const char *cfile)
    548 {
    549   JBGroupResource *gres = JB_GROUP_RESOURCE(object);
    550   JBObjectClass *object_class;
    551   char *depsfile;
    552   char *depsflags = NULL;
    553 
    554   object_class = JB_OBJECT_GET_CLASS(object);
    555 
    556   depsfile = object_file_get_depsfile(ofile);
    557 
    558   if (jb_variable_get_bool("cc-dependency-tracking"))
    559     depsflags = jb_variable_expand(" -MT $ofile -MD -MP -MF $depsfile",
    560 				   "ofile", ofile,
    561 				   "depsfile", depsfile,
    562 				   NULL);
    563 
    564   jb_mkdir_of_file(ofile);
    565 
    566   jb_action_exec("$cc -c -o $ofile"
    567 		 " $package-cflags $group-cflags $object-cflags $stock-cflags $cflags"
    568 		 " $package-cppflags $group-cppflags $object-cppflags $stock-cppflags $cppflags"
    569 		 " $depsflags $cfile",
    570 		 "package-cflags", jb_compile_options_get_cflags(jb_compile_options),
    571 		 "group-cflags", jb_compile_options_get_cflags(gres->group->compile_options),
    572 		 "object-cflags", jb_compile_options_get_cflags(object->compile_options),
    573 		 "stock-cflags", object_class->stock_cflags,
    574 		 "package-cppflags", jb_compile_options_get_cppflags(jb_compile_options),
    575 		 "group-cppflags", jb_compile_options_get_cppflags(gres->group->compile_options),
    576 		 "object-cppflags", jb_compile_options_get_cppflags(object->compile_options),
    577 		 "stock-cppflags", object_class->stock_cppflags,
    578 		 "depsflags", depsflags,
    579 		 "ofile", ofile,
    580 		 "cfile", cfile,
    581 		 NULL);
    582 
    583   g_free(depsfile);
    584   g_free(depsflags);
    585 }
    586 
    587 static void
    588 object_file_clean (const char *ofile)
    589 {
    590   char *depsfile;
    591 
    592   depsfile = object_file_get_depsfile(ofile);
    593 
    594   jb_action_rm(ofile);
    595   jb_action_rm(depsfile);
    596 
    597   g_free(depsfile);
    598 }
    599 
    600 G_DEFINE_TYPE(JBSource, jb_source, JB_TYPE_OBJECT_RESOURCE)
    601 
    602 JBSource *
    603 jb_source_new (const char *filename)
    604 {
    605   JBSource *self;
    606 
    607   g_return_val_if_fail(filename != NULL, NULL);
    608 
    609   self = g_object_new(JB_TYPE_SOURCE, NULL);
    610   self->filename = g_strdup(filename);
    611 
    612   return self;
    613 }
    614 
    615 static char *
    616 source_to_string (JBResource *res)
    617 {
    618   JBSource *self = JB_SOURCE(res);
    619   char *base_filename;
    620   char *str;
    621 
    622   base_filename = g_path_get_basename(self->filename);
    623   str = g_strdup_printf("C source %s", base_filename);
    624   g_free(base_filename);
    625 
    626   return str;
    627 }
    628 
    629 static void
    630 source_get_files (JBSource *self, char **ofile, char **cfile)
    631 {
    632   JBObjectResource *ores = JB_OBJECT_RESOURCE(self);
    633   JBGroupResource *gres = JB_GROUP_RESOURCE(ores->object);
    634   char *base_filename;
    635   char *base_prefix;
    636 
    637   base_filename = g_path_get_basename(self->filename);
    638   base_prefix = jb_strip_extension(base_filename);
    639 
    640   if (ofile != NULL)
    641     *ofile = g_strdup_printf("%s/%s-%s.o",
    642 			     gres->group->builddir,
    643 			     ores->object->name,
    644 			     base_prefix);
    645   if (cfile != NULL)
    646     *cfile = g_strdup_printf("%s/%s", gres->group->srcdir, self->filename);
    647 
    648   g_free(base_filename);
    649   g_free(base_prefix);
    650 }
    651 
    652 static void
    653 source_build (JBResource *res)
    654 {
    655   JBObjectResource *ores = JB_OBJECT_RESOURCE(res);
    656   JBSource *self = JB_SOURCE(res);
    657   char *ofile;
    658   char *cfile;
    659 
    660   source_get_files(self, &ofile, &cfile);
    661 
    662   if (! object_file_is_uptodate(ofile, cfile))
    663     {
    664       jb_resource_message_building(res);
    665       object_file_build(ores->object, ofile, cfile);
    666     }
    667 
    668   g_free(ofile);
    669   g_free(cfile);
    670 }
    671 
    672 static void
    673 source_makedist (JBResource *res)
    674 {
    675   JBSource *self = JB_SOURCE(res);
    676   char *cfile;
    677 
    678   source_get_files(self, NULL, &cfile);
    679 
    680   jb_action_add_to_dist(cfile);
    681 
    682   g_free(cfile);
    683 }
    684 
    685 static void
    686 source_clean (JBResource *res)
    687 {
    688   JBSource *self = JB_SOURCE(res);
    689   char *ofile;
    690 
    691   source_get_files(self, &ofile, NULL);
    692 
    693   object_file_clean(ofile);
    694 
    695   g_free(ofile);
    696 }
    697 
    698 static char *
    699 source_get_object_file (JBObjectResource *res)
    700 {
    701   JBSource *self = JB_SOURCE(res);
    702   char *ofile;
    703 
    704   source_get_files(self, &ofile, NULL);
    705 
    706   return ofile;
    707 }
    708 
    709 static void
    710 jb_source_init (JBSource *self)
    711 {
    712 }
    713 
    714 static void
    715 jb_source_class_init (JBSourceClass *class)
    716 {
    717   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
    718   JBObjectResourceClass *orclass = JB_OBJECT_RESOURCE_CLASS(class);
    719 
    720   rclass->to_string = source_to_string;
    721   rclass->build = source_build;
    722   rclass->makedist = source_makedist;
    723   rclass->clean = source_clean;
    724 
    725   orclass->get_object_file = source_get_object_file;
    726 }
    727 
    728 G_DEFINE_TYPE(JBGobClass, jb_gob_class, JB_TYPE_OBJECT_RESOURCE)
    729 
    730 JBGobClass *
    731 jb_gob_class_new (const char *name)
    732 {
    733   JBGobClass *self;
    734 
    735   g_return_val_if_fail(name != NULL, NULL);
    736   g_return_val_if_fail(jb_feature_is_enabled(&jb_gob2_feature), NULL);
    737 
    738   self = g_object_new(JB_TYPE_GOB_CLASS, NULL);
    739   self->name = g_strdup(name);
    740 
    741   return self;
    742 }
    743 
    744 static void
    745 gob_class_get_gob_files (JBGobClass *self, char **gobfile, char **stamp)
    746 {
    747   JBGroupResource *gres = JB_GROUP_RESOURCE(JB_OBJECT_RESOURCE(self)->object);
    748   char *prefix;
    749 
    750   prefix = g_strdelimit(g_ascii_strdown(self->name, -1), ":", '-');
    751   if (gobfile != NULL)
    752     *gobfile = g_strdup_printf("%s/%s.gob", gres->group->srcdir, prefix);
    753   if (stamp != NULL)
    754     *stamp = g_strdup_printf("%s/%s.gob.stamp", gres->group->builddir, prefix);
    755   g_free(prefix);
    756 }
    757 
    758 static void
    759 gob_class_pre_build (JBResource *res)
    760 {
    761   JBGobClass *self = JB_GOB_CLASS(res);
    762   char *gobfile;
    763   char *stamp;
    764 
    765   gob_class_get_gob_files(self, &gobfile, &stamp);
    766 
    767   if (! jb_is_uptodate(stamp, gobfile))
    768     {
    769       JBObjectResource *ores = JB_OBJECT_RESOURCE(res);
    770       JBGroupResource *gres = JB_GROUP_RESOURCE(ores->object);
    771       const char *error;
    772 
    773       error = jb_variable_get_string_or_null("gob2-error");
    774       if (error != NULL)
    775 	jb_error("%s", error);
    776 
    777       jb_message("building GOB class %s", self->name);
    778 
    779       jb_mkdir(gres->group->builddir);
    780 
    781       jb_action_exec("$gob2 $package-gob2flags $group-gob2flags $object-gob2flags $gobfile",
    782 		     "package-gob2flags", jb_compile_options_get_gob2flags(jb_compile_options),
    783 		     "group-gob2flags", jb_compile_options_get_gob2flags(gres->group->compile_options),
    784 		     "object-gob2flags", jb_compile_options_get_gob2flags(ores->object->compile_options),
    785 		     "gobfile", gobfile,
    786 		     NULL);
    787 
    788       jb_action_exec("touch $stamp",
    789 		     "stamp", stamp,
    790 		     NULL);
    791     }
    792 
    793   g_free(gobfile);
    794   g_free(stamp);
    795 }
    796 
    797 static void
    798 gob_class_get_c_files (JBGobClass *self,
    799 		       char **ofile,
    800 		       char **cfile,
    801 		       char **hfile,
    802 		       char **phfile)
    803 {
    804   JBObjectResource *ores = JB_OBJECT_RESOURCE(self);
    805   JBGroupResource *gres = JB_GROUP_RESOURCE(ores->object);
    806   char *prefix;
    807 
    808   prefix = g_strdelimit(g_ascii_strdown(self->name, -1), ":", '-');
    809 
    810   if (ofile != NULL)
    811     *ofile = g_strdup_printf("%s/%s-%s.o",
    812 			     gres->group->builddir,
    813 			     ores->object->name,
    814 			     prefix);
    815   if (cfile != NULL)
    816     *cfile = g_strdup_printf("%s/%s.c", gres->group->builddir, prefix);
    817   if (hfile != NULL)
    818     *hfile = g_strdup_printf("%s/%s.h", gres->group->builddir, prefix);
    819   if (phfile != NULL)
    820     *phfile = g_strdup_printf("%s/%s-private.h", gres->group->builddir, prefix);
    821 
    822   g_free(prefix);
    823 }
    824 
    825 static void
    826 gob_class_build (JBResource *res)
    827 {
    828   JBGobClass *self = JB_GOB_CLASS(res);
    829   char *ofile;
    830   char *cfile;
    831 
    832   gob_class_get_c_files(self, &ofile, &cfile, NULL, NULL);
    833 
    834   if (! object_file_is_uptodate(ofile, cfile))
    835     {
    836       JBObjectResource *ores = JB_OBJECT_RESOURCE(res);
    837       char *class_name;
    838 
    839       class_name = jb_strip_chars(self->name, ":");
    840       jb_message("building class %s", class_name);
    841       g_free(class_name);
    842 
    843       object_file_build(ores->object, ofile, cfile);
    844     }
    845 
    846   g_free(ofile);
    847   g_free(cfile);
    848 }
    849 
    850 static void
    851 gob_class_makedist (JBResource *res)
    852 {
    853   JBGobClass *self = JB_GOB_CLASS(res);
    854   char *gobfile;
    855   char *stamp;
    856   char *cfile;
    857   char *hfile;
    858   char *phfile;
    859 
    860   gob_class_get_gob_files(self, &gobfile, &stamp);
    861   gob_class_get_c_files(self, NULL, &cfile, &hfile, &phfile);
    862 
    863   jb_action_add_to_dist(gobfile);
    864   jb_action_add_to_dist(stamp);
    865   jb_action_add_to_dist(cfile);
    866   jb_action_add_to_dist(hfile);
    867   jb_action_add_to_dist(phfile);
    868 
    869   g_free(gobfile);
    870   g_free(stamp);
    871   g_free(cfile);
    872   g_free(hfile);
    873   g_free(phfile);
    874 }
    875 
    876 static void
    877 gob_class_clean (JBResource *res)
    878 {
    879   JBGobClass *self = JB_GOB_CLASS(res);
    880   char *ofile;
    881 
    882   gob_class_get_c_files(self, &ofile, NULL, NULL, NULL);
    883 
    884   object_file_clean(ofile);
    885 
    886   g_free(ofile);
    887 }
    888 
    889 static void
    890 gob_class_maintainerclean (JBResource *res)
    891 {
    892   JBGobClass *self = JB_GOB_CLASS(res);
    893   char *stamp;
    894   char *cfile;
    895   char *hfile;
    896   char *phfile;
    897 
    898   gob_class_get_gob_files(self, NULL, &stamp);
    899   gob_class_get_c_files(self, NULL, &cfile, &hfile, &phfile);
    900 
    901   jb_action_rm(stamp);
    902   jb_action_rm(cfile);
    903   jb_action_rm(hfile);
    904   jb_action_rm(phfile);
    905 
    906   g_free(stamp);
    907   g_free(cfile);
    908   g_free(hfile);
    909   g_free(phfile);
    910 }
    911 
    912 static char *
    913 gob_class_get_object_file (JBObjectResource *res)
    914 {
    915   JBGobClass *self = JB_GOB_CLASS(res);
    916   char *ofile;
    917 
    918   gob_class_get_c_files(self, &ofile, NULL, NULL, NULL);
    919 
    920   return ofile;
    921 }
    922 
    923 static void
    924 jb_gob_class_init (JBGobClass *self)
    925 {
    926 }
    927 
    928 static void
    929 jb_gob_class_class_init (JBGobClassClass *class)
    930 {
    931   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
    932   JBObjectResourceClass *orclass = JB_OBJECT_RESOURCE_CLASS(class);
    933 
    934   rclass->pre_build = gob_class_pre_build;
    935   rclass->build = gob_class_build;
    936   rclass->makedist = gob_class_makedist;
    937   rclass->clean = gob_class_clean;
    938   rclass->maintainerclean = gob_class_maintainerclean;
    939 
    940   orclass->get_object_file = gob_class_get_object_file;
    941 }
    942 
    943 G_DEFINE_TYPE(JBIntltoolFile, jb_intltool_file, JB_TYPE_GROUP_RESOURCE)
    944 
    945 JBIntltoolFile *
    946 jb_intltool_file_new (const char *type,
    947 		      const char *filename,
    948 		      const char *merge_flags)
    949 {
    950   JBIntltoolFile *self;
    951 
    952   g_return_val_if_fail(type != NULL, NULL);
    953   g_return_val_if_fail(filename != NULL, NULL);
    954   g_return_val_if_fail(g_str_has_suffix(filename, ".in"), NULL);
    955   g_return_val_if_fail(merge_flags != NULL, NULL);
    956 
    957   self = g_object_new(JB_TYPE_INTLTOOL_FILE, NULL);
    958   self->type = g_strdup(type);
    959   self->filename = g_strdup(filename);
    960   self->merge_flags = g_strdup(merge_flags);
    961 
    962   return self;
    963 }
    964 
    965 static char *
    966 intltool_file_to_string (JBResource *res)
    967 {
    968   JBIntltoolFile *self = JB_INTLTOOL_FILE(res);
    969 
    970   return g_strdup_printf("%s %s", self->type, self->filename);
    971 }
    972 
    973 static void
    974 intltool_file_get_files (JBIntltoolFile *self, char **infile, char **outfile)
    975 {
    976   JBGroupResource *gres = JB_GROUP_RESOURCE(self);
    977   char *filename;
    978 
    979   filename = g_strdup_printf("%s/%s", gres->group->srcdir, self->filename);
    980 
    981   if (infile != NULL)
    982     *infile = get_input_file(filename);
    983   if (outfile != NULL)
    984     *outfile = get_output_file(filename);
    985 
    986   g_free(filename);
    987 }
    988 
    989 static void
    990 intltool_file_build (JBResource *res)
    991 {
    992   JBIntltoolFile *self = JB_INTLTOOL_FILE(res);
    993   char *infile;
    994   char *outfile;
    995   GSList *deps = NULL;
    996 
    997   if (! jb_is_uptodate(INTLTOOL_MERGE_OUT, INTLTOOL_MERGE_IN))
    998     {
    999       GHashTable *variables;
   1000 
   1001       jb_message("building intltool-merge");
   1002 
   1003       jb_mkdir_of_file(INTLTOOL_MERGE_OUT);
   1004 
   1005       variables = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
   1006       g_hash_table_insert(variables, "INTLTOOL_PERL", jb_variable_expand("$perl", NULL));
   1007       g_hash_table_insert(variables, "INTLTOOL_LIBDIR", jb_variable_expand("$perl", NULL));
   1008 
   1009       jb_subst(INTLTOOL_MERGE_IN, INTLTOOL_MERGE_OUT, variables);
   1010 
   1011       g_hash_table_destroy(variables);
   1012 
   1013       jb_chmod(INTLTOOL_MERGE_OUT, 0755);
   1014     }
   1015 
   1016   intltool_file_get_files(self, &infile, &outfile);
   1017 
   1018   deps = g_slist_append(deps, g_strdup(infile));
   1019   deps = g_slist_append(deps, g_strdup(INTLTOOL_MERGE_OUT));
   1020   deps = g_slist_concat(deps, jb_match_files("po/*.po"));
   1021 
   1022   if (! jb_is_uptodate_list(outfile, deps))
   1023     {
   1024       jb_resource_message_building(res);
   1025 
   1026       jb_mkdir_of_file(outfile);
   1027       jb_mkdir_of_file(INTLTOOL_MERGE_CACHE);
   1028 
   1029       jb_action_exec("LC_ALL=C " INTLTOOL_MERGE_OUT " $merge-flags -u -c " INTLTOOL_MERGE_CACHE " po $infile $outfile",
   1030 		     "merge-flags", self->merge_flags,
   1031 		     "infile", infile,
   1032 		     "outfile", outfile,
   1033 		     NULL);
   1034     }
   1035 
   1036   g_free(infile);
   1037   g_free(outfile);
   1038   jb_g_slist_free_deep(deps);
   1039 }
   1040 
   1041 static void
   1042 intltool_file_install (JBResource *res)
   1043 {
   1044   JBIntltoolFile *self = JB_INTLTOOL_FILE(res);
   1045   char *outfile;
   1046 
   1047   intltool_file_get_files(self, NULL, &outfile);
   1048 
   1049   jb_install_options_install_data(self->install_options, outfile, NULL);
   1050 
   1051   g_free(outfile);
   1052 }
   1053 
   1054 static void
   1055 intltool_file_makedist (JBResource *res)
   1056 {
   1057   JBIntltoolFile *self = JB_INTLTOOL_FILE(res);
   1058   JBGroupResource *gres = JB_GROUP_RESOURCE(res);
   1059   char *filename;
   1060 
   1061   filename = g_strdup_printf("%s/%s", gres->group->srcdir, self->filename);
   1062 
   1063   jb_action_add_to_dist(INTLTOOL_MERGE_IN);
   1064 
   1065   if (! has_template(filename))
   1066     jb_action_add_to_dist(filename);
   1067 
   1068   g_free(filename);
   1069 }
   1070 
   1071 static void
   1072 intltool_file_clean (JBResource *res)
   1073 {
   1074   JBIntltoolFile *self = JB_INTLTOOL_FILE(res);
   1075   char *outfile;
   1076 
   1077   intltool_file_get_files(self, NULL, &outfile);
   1078 
   1079   jb_action_rm(INTLTOOL_MERGE_OUT);
   1080   jb_action_rm(INTLTOOL_MERGE_CACHE);
   1081   jb_action_rm(outfile);
   1082 
   1083   g_free(outfile);
   1084 }
   1085 
   1086 static void
   1087 intltool_file_maintainerclean (JBResource *res)
   1088 {
   1089   jb_action_rm(INTLTOOL_MERGE_IN);
   1090 }
   1091 
   1092 static void
   1093 jb_intltool_file_init (JBIntltoolFile *self)
   1094 {
   1095   self->install_options = jb_install_options_new();
   1096 }
   1097 
   1098 static void
   1099 jb_intltool_file_class_init (JBIntltoolFileClass *class)
   1100 {
   1101   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   1102 
   1103   rclass->to_string = intltool_file_to_string;
   1104   rclass->build = intltool_file_build;
   1105   rclass->install = intltool_file_install;
   1106   rclass->makedist = intltool_file_makedist;
   1107   rclass->clean = intltool_file_clean;
   1108   rclass->maintainerclean = intltool_file_maintainerclean;
   1109 }
   1110 
   1111 G_DEFINE_TYPE(JBGConfSchemas, jb_gconf_schemas, JB_TYPE_INTLTOOL_FILE)
   1112 
   1113 JBGConfSchemas *
   1114 jb_gconf_schemas_new (const char *filename)
   1115 {
   1116   JBGConfSchemas *self;
   1117   JBIntltoolFile *intltool_file;
   1118 
   1119   self = g_object_new(JB_TYPE_GCONF_SCHEMAS, NULL);
   1120 
   1121   intltool_file = JB_INTLTOOL_FILE(self);
   1122 
   1123   intltool_file->type = g_strdup("GConf schemas");
   1124   intltool_file->filename = g_strdup(filename);
   1125   intltool_file->merge_flags = g_strdup("-s");
   1126 
   1127   return self;
   1128 }
   1129 
   1130 static void
   1131 gconf_schemas_install (JBResource *res)
   1132 {
   1133   JBIntltoolFile *intltool_file = JB_INTLTOOL_FILE(res);
   1134 
   1135   JB_RESOURCE_CLASS(jb_gconf_schemas_parent_class)->install(res);
   1136 
   1137   if (jb_variable_get_bool("install-gconf-schemas"))
   1138     {
   1139       char *outfile;
   1140       char *filename;
   1141 
   1142       intltool_file_get_files(intltool_file, NULL, &outfile);
   1143 
   1144       filename = g_path_get_basename(outfile);
   1145       jb_message("installing GConf schemas %s", filename);
   1146       g_free(filename);
   1147 
   1148       jb_action_exec("GCONF_CONFIG_SOURCE=$gconf-config-source $gconftool-2 --makefile-install-rule $outfile",
   1149 		     "outfile", outfile,
   1150 		     NULL);
   1151 
   1152       g_free(outfile);
   1153     }
   1154 }
   1155 
   1156 static void
   1157 jb_gconf_schemas_init (JBGConfSchemas *self)
   1158 {
   1159   JBIntltoolFile *intltool_file = JB_INTLTOOL_FILE(self);
   1160 
   1161   jb_install_options_set_installdir(intltool_file->install_options, "$gconf-schemas-dir");
   1162 }
   1163 
   1164 static void
   1165 jb_gconf_schemas_class_init (JBGConfSchemasClass *class)
   1166 {
   1167   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   1168 
   1169   rclass->install = gconf_schemas_install;
   1170 }
   1171 
   1172 G_DEFINE_TYPE(JBGnomeHelp, jb_gnome_help, JB_TYPE_GROUP_RESOURCE)
   1173 
   1174 JBGnomeHelp *
   1175 jb_gnome_help_new (const char *lang, const char *entities)
   1176 {
   1177   JBGnomeHelp *self;
   1178 
   1179   g_return_val_if_fail(lang != NULL, NULL);
   1180   g_return_val_if_fail(jb_feature_is_enabled(&jb_gnome_help_feature), NULL);
   1181 
   1182   self = g_object_new(JB_TYPE_GNOME_HELP, NULL);
   1183   self->name = g_strdup(jb_variable_get_string("package"));
   1184   self->lang = g_strdup(lang);
   1185   self->entities = g_strsplit(entities != NULL ? entities : "", " ", 0);
   1186 
   1187   return self;
   1188 }
   1189 
   1190 static char *
   1191 gnome_help_to_string (JBResource *res)
   1192 {
   1193   JBGnomeHelp *self = JB_GNOME_HELP(res);
   1194 
   1195   return g_strdup_printf("GNOME help %s [%s]", self->name, self->lang);
   1196 }
   1197 
   1198 static void
   1199 gnome_help_get_files (JBGnomeHelp *self,
   1200 		      char **inomf,
   1201 		      char **outomf,
   1202 		      char **helpdir,
   1203 		      char **xmlfile,
   1204 		      GSList **entities,
   1205 		      GSList **figures)
   1206 {
   1207   JBGroupResource *gres = JB_GROUP_RESOURCE(self);
   1208   char *_inomf;
   1209 
   1210   _inomf = g_strdup_printf("%s/%s/%s-%s.omf.in",
   1211 			   gres->group->srcdir,
   1212 			   self->lang,
   1213 			   self->name,
   1214 			   self->lang);
   1215 
   1216   if (inomf != NULL)
   1217     *inomf = g_strdup(_inomf);
   1218   if (outomf != NULL)
   1219     *outomf = get_output_file(_inomf);
   1220   if (helpdir != NULL)
   1221     *helpdir = jb_variable_expand("$help-dir/$name/$lang",
   1222 				  "name", self->name,
   1223 				  "lang", self->lang,
   1224 				  NULL);
   1225   if (xmlfile != NULL)
   1226     *xmlfile = g_strdup_printf("%s/%s/%s.xml",
   1227 			       gres->group->srcdir,
   1228 			       self->lang,
   1229 			       self->name);
   1230   if (entities != NULL)
   1231     {
   1232       int i;
   1233       GSList *list = NULL;
   1234 
   1235       for (i = 0; self->entities[i] != NULL; i++)
   1236 	list = g_slist_append(list, g_strdup_printf("%s/%s/%s",
   1237 						    gres->group->srcdir,
   1238 						    self->lang,
   1239 						    self->entities[i]));
   1240 
   1241       *entities = list;
   1242     }
   1243   if (figures != NULL)
   1244     {
   1245       char *pattern;
   1246 
   1247       pattern = g_strdup_printf("%s/%s/figures/*.png",
   1248 				gres->group->srcdir,
   1249 				self->lang);
   1250       *figures = jb_match_files(pattern);
   1251       g_free(pattern);
   1252     }
   1253 
   1254   g_free(_inomf);
   1255 }
   1256 
   1257 static void
   1258 gnome_help_build (JBResource *res)
   1259 {
   1260   JBGnomeHelp *self = JB_GNOME_HELP(res);
   1261   char *inomf;
   1262   char *outomf;
   1263   char *helpdir;
   1264 
   1265   gnome_help_get_files(self, &inomf, &outomf, &helpdir, NULL, NULL, NULL);
   1266 
   1267   if (! jb_is_uptodate(outomf, inomf))
   1268     {
   1269       jb_resource_message_building(res);
   1270 
   1271       jb_mkdir_of_file(outomf);
   1272 
   1273       jb_action_exec("$scrollkeeper-preinstall \"$helpdir/$helpname.xml\" $inomf $outomf",
   1274 		     "helpdir", helpdir,
   1275 		     "helpname", self->name,
   1276 		     "inomf", inomf,
   1277 		     "outomf", outomf,
   1278 		     NULL);
   1279     }
   1280 
   1281   g_free(inomf);
   1282   g_free(outomf);
   1283   g_free(helpdir);
   1284 }
   1285 
   1286 static void
   1287 gnome_help_install (JBResource *res)
   1288 {
   1289   JBGnomeHelp *self = JB_GNOME_HELP(res);
   1290   char *outomf;
   1291   char *helpdir;
   1292   char *omfdir;
   1293   char *xmlfile;
   1294   GSList *entities;
   1295   GSList *figures;
   1296   char *figdir;
   1297 
   1298   gnome_help_get_files(self, NULL, &outomf, &helpdir, &xmlfile, &entities, &figures);
   1299 
   1300   omfdir = g_strdup_printf("$omf-dir/%s", self->name);
   1301 
   1302   figdir = g_strdup_printf("%s/figures", helpdir);
   1303 
   1304   jb_action_install_data(xmlfile, helpdir);
   1305 
   1306   jb_action_install_data_list(entities, helpdir);
   1307 
   1308   jb_action_install_data_list(figures, figdir);
   1309 
   1310   jb_action_install_data(outomf, omfdir);
   1311 
   1312   jb_action_exec("-$scrollkeeper-update -p \"$destdir$scrollkeeper-dir\" -o \"$destdir$omfdir\"",
   1313 		 "omfdir", omfdir,
   1314 		 NULL);
   1315 
   1316   g_free(outomf);
   1317   g_free(helpdir);
   1318   g_free(omfdir);
   1319   g_free(xmlfile);
   1320   jb_g_slist_free_deep(entities);
   1321   jb_g_slist_free_deep(figures);
   1322   g_free(figdir);
   1323 }
   1324 
   1325 static void
   1326 gnome_help_makedist (JBResource *res)
   1327 {
   1328   JBGnomeHelp *self = JB_GNOME_HELP(res);
   1329   char *inomf;
   1330   char *xmlfile;
   1331   GSList *entities;
   1332   GSList *figures;
   1333 
   1334   gnome_help_get_files(self, &inomf, NULL, NULL, &xmlfile, &entities, &figures);
   1335 
   1336   jb_action_add_to_dist(inomf);
   1337   jb_action_add_to_dist(xmlfile);
   1338   jb_action_add_to_dist_list(entities);
   1339   jb_action_add_to_dist_list(figures);
   1340 
   1341   g_free(inomf);
   1342   g_free(xmlfile);
   1343   jb_g_slist_free_deep(entities);
   1344   jb_g_slist_free_deep(figures);
   1345 }
   1346 
   1347 static void
   1348 gnome_help_clean (JBResource *res)
   1349 {
   1350   JBGnomeHelp *self = JB_GNOME_HELP(res);
   1351   char *outomf;
   1352 
   1353   gnome_help_get_files(self, NULL, &outomf, NULL, NULL, NULL, NULL);
   1354 
   1355   jb_action_rm(outomf);
   1356 
   1357   g_free(outomf);
   1358 }
   1359 
   1360 static void
   1361 jb_gnome_help_init (JBGnomeHelp *self)
   1362 {
   1363 }
   1364 
   1365 static void
   1366 jb_gnome_help_class_init (JBGnomeHelpClass *class)
   1367 {
   1368   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   1369 
   1370   rclass->to_string = gnome_help_to_string;
   1371   rclass->build = gnome_help_build;
   1372   rclass->install = gnome_help_install;
   1373   rclass->makedist = gnome_help_makedist;
   1374   rclass->clean = gnome_help_clean;
   1375 }
   1376 
   1377 G_DEFINE_TYPE(JBTranslations, jb_translations, JB_TYPE_GROUP_RESOURCE)
   1378 
   1379 static void
   1380 translations_get_files (JBTranslations *self, char **infile, char **outfile)
   1381 {
   1382   JBGroupResource *gres = JB_GROUP_RESOURCE(self);
   1383 
   1384   if (infile != NULL)
   1385     *infile = g_strdup_printf("%s/%s.po", gres->group->srcdir, self->lang);
   1386   if (outfile != NULL)
   1387     *outfile = g_strdup_printf("%s/%s.mo", gres->group->builddir, self->lang);
   1388 }
   1389 
   1390 JBTranslations *
   1391 jb_translations_new (const char *lang)
   1392 {
   1393   JBTranslations *self;
   1394 
   1395   g_return_val_if_fail(lang != NULL, NULL);
   1396   g_return_val_if_fail(jb_feature_is_enabled(&jb_gettext_feature), NULL);
   1397 
   1398   self = g_object_new(JB_TYPE_TRANSLATIONS, NULL);
   1399   self->lang = g_strdup(lang);
   1400 
   1401   return self;
   1402 }
   1403 
   1404 static char *
   1405 translations_to_string (JBResource *res)
   1406 {
   1407   JBTranslations *self = JB_TRANSLATIONS(res);
   1408 
   1409   return g_strdup_printf("%s translations", self->lang);
   1410 }
   1411 
   1412 static void
   1413 translations_build (JBResource *res)
   1414 {
   1415   JBTranslations *self = JB_TRANSLATIONS(res);
   1416   char *infile;
   1417   char *outfile;
   1418 
   1419   translations_get_files(self, &infile, &outfile);
   1420 
   1421   if (! jb_is_uptodate(outfile, infile))
   1422     {
   1423       jb_resource_message_building(res);
   1424 
   1425       jb_mkdir_of_file(outfile);
   1426 
   1427       jb_action_exec("$msgfmt -o $outfile.tmp $infile && mv -f $outfile.tmp $outfile",
   1428 		     "outfile", outfile,
   1429 		     "infile", infile,
   1430 		     NULL);
   1431     }
   1432 
   1433   g_free(infile);
   1434   g_free(outfile);
   1435 }
   1436 
   1437 static void
   1438 translations_install (JBResource *res)
   1439 {
   1440   JBTranslations *self = JB_TRANSLATIONS(res);
   1441   char *outfile;
   1442   char *installfile;
   1443 
   1444   translations_get_files(self, NULL, &outfile);
   1445 
   1446   installfile = g_strdup_printf("$prefix/share/locale/%s/LC_MESSAGES/$package.mo", self->lang);
   1447 
   1448   jb_action_install_data_to_file(outfile, installfile);
   1449 
   1450   g_free(outfile);
   1451   g_free(installfile);
   1452 }
   1453 
   1454 static void
   1455 translations_makedist (JBResource *res)
   1456 {
   1457   JBTranslations *self = JB_TRANSLATIONS(res);
   1458   char *infile;
   1459 
   1460   translations_get_files(self, &infile, NULL);
   1461 
   1462   jb_action_add_to_dist(infile);
   1463 
   1464   g_free(infile);
   1465 }
   1466 
   1467 static void
   1468 translations_clean (JBResource *res)
   1469 {
   1470   JBTranslations *self = JB_TRANSLATIONS(res);
   1471   char *outfile;
   1472 
   1473   translations_get_files(self, NULL, &outfile);
   1474 
   1475   jb_action_rm(outfile);
   1476 
   1477   g_free(outfile);
   1478 }
   1479 
   1480 static void
   1481 jb_translations_init (JBTranslations *self)
   1482 {
   1483 }
   1484 
   1485 static void
   1486 jb_translations_class_init (JBTranslationsClass *class)
   1487 {
   1488   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   1489 
   1490   rclass->to_string = translations_to_string;
   1491   rclass->build = translations_build;
   1492   rclass->install = translations_install;
   1493   rclass->makedist = translations_makedist;
   1494   rclass->clean = translations_clean;
   1495 }
   1496 
   1497 G_DEFINE_TYPE(JBRule, jb_rule, JB_TYPE_GROUP_RESOURCE)
   1498 
   1499 JBRule *
   1500 jb_rule_new (void)
   1501 {
   1502   return g_object_new(JB_TYPE_RULE, NULL);
   1503 }
   1504 
   1505 /*
   1506  * This could be inferred by mapping input files to the resources that
   1507  * produce them, but it would require a more complex JBResource
   1508  * design. For now this is good enough.
   1509  */
   1510 void
   1511 jb_rule_add_dependency (JBRule *self, JBGroupResource *res)
   1512 {
   1513   g_return_if_fail(JB_IS_RULE(self));
   1514   g_return_if_fail(JB_IS_GROUP_RESOURCE(res));
   1515 
   1516   self->dependencies = g_slist_append(self->dependencies, g_object_ref(res));
   1517 }
   1518 
   1519 void
   1520 jb_rule_add_input_file (JBRule *self, const char *format, ...)
   1521 {
   1522   char *file;
   1523 
   1524   g_return_if_fail(JB_IS_RULE(self));
   1525   g_return_if_fail(format != NULL);
   1526 
   1527   JB_STRDUP_VPRINTF(file, format);
   1528   self->input_files = g_slist_append(self->input_files, file);
   1529 }
   1530 
   1531 void
   1532 jb_rule_add_output_file (JBRule *self, const char *format, ...)
   1533 {
   1534   char *file;
   1535 
   1536   g_return_if_fail(JB_IS_RULE(self));
   1537   g_return_if_fail(format != NULL);
   1538 
   1539   JB_STRDUP_VPRINTF(file, format);
   1540   self->output_files = g_slist_append(self->output_files, file);
   1541 }
   1542 
   1543 void
   1544 jb_rule_set_build_message (JBRule *self, const char *format, ...)
   1545 {
   1546   g_return_if_fail(JB_IS_RULE(self));
   1547   g_return_if_fail(format != NULL);
   1548 
   1549   g_free(self->build_message);
   1550   JB_STRDUP_VPRINTF(self->build_message, format);
   1551 }
   1552 
   1553 void
   1554 jb_rule_add_build_command (JBRule *self, const char *format, ...)
   1555 {
   1556   char *command;
   1557 
   1558   g_return_if_fail(JB_IS_RULE(self));
   1559   g_return_if_fail(format != NULL);
   1560 
   1561   JB_STRDUP_VPRINTF(command, format);
   1562   self->build_commands = g_slist_append(self->build_commands, command);
   1563 }
   1564 
   1565 void
   1566 jb_rule_set_install_message (JBRule *self, const char *format, ...)
   1567 {
   1568   g_return_if_fail(JB_IS_RULE(self));
   1569   g_return_if_fail(format != NULL);
   1570 
   1571   g_free(self->install_message);
   1572   JB_STRDUP_VPRINTF(self->install_message, format);
   1573 }
   1574 
   1575 void
   1576 jb_rule_add_install_command (JBRule *self, const char *format, ...)
   1577 {
   1578   char *command;
   1579 
   1580   g_return_if_fail(JB_IS_RULE(self));
   1581   g_return_if_fail(format != NULL);
   1582 
   1583   JB_STRDUP_VPRINTF(command, format);
   1584   self->install_commands = g_slist_append(self->install_commands, command);
   1585 }
   1586 
   1587 static GSList *
   1588 rule_expand_variables (JBRule *self, GSList *strings)
   1589 {
   1590   JBGroupResource *gres = JB_GROUP_RESOURCE(self);
   1591   GSList *result = NULL;
   1592   GSList *l;
   1593 
   1594   JB_LIST_FOREACH(l, strings)
   1595     {
   1596       const char *str = l->data;
   1597       char *expanded;
   1598 
   1599       expanded = jb_variable_expand(str,
   1600 				    "srcdir", gres->group->srcdir,
   1601 				    "builddir", gres->group->builddir,
   1602 				    NULL);
   1603 
   1604       result = g_slist_append(result, expanded);
   1605     }
   1606 
   1607   return result;
   1608 }
   1609 
   1610 static void
   1611 rule_perform (JBRule *self, const char *message, GSList *commands)
   1612 {
   1613   JBGroupResource *gres = JB_GROUP_RESOURCE(self);
   1614   GSList *l;
   1615 
   1616   if (commands == NULL)
   1617     return;
   1618 
   1619   jb_message("%s", message);
   1620 
   1621   JB_LIST_FOREACH(l, commands)
   1622     {
   1623       const char *command = l->data;
   1624 
   1625       jb_action_exec(command,
   1626 		     "srcdir", gres->group->srcdir,
   1627 		     "builddir", gres->group->builddir,
   1628 		     NULL);
   1629     }
   1630 }
   1631 
   1632 static void
   1633 rule_build (JBResource *res)
   1634 {
   1635   JBRule *self = JB_RULE(res);
   1636   GSList *l;
   1637 
   1638   JB_LIST_FOREACH(l, self->dependencies)
   1639     {
   1640       JBResource *dep = l->data;
   1641       jb_resource_build(dep);
   1642     }
   1643 
   1644   if (self->output_files != NULL)
   1645     {
   1646       GSList *input_files;
   1647       GSList *output_files;
   1648       gboolean is_uptodate;
   1649 
   1650       input_files = rule_expand_variables(self, self->input_files);
   1651       output_files = rule_expand_variables(self, self->output_files);
   1652 
   1653       is_uptodate = jb_is_uptodate_list_list(output_files, input_files);
   1654 
   1655       jb_g_slist_free_deep(input_files);
   1656       jb_g_slist_free_deep(output_files);
   1657 
   1658       if (is_uptodate)
   1659 	return;
   1660     }
   1661 
   1662   rule_perform(self, self->build_message, self->build_commands);
   1663 }
   1664 
   1665 static void
   1666 rule_install (JBResource *res)
   1667 {
   1668   JBRule *self = JB_RULE(res);
   1669 
   1670   rule_perform(self, self->install_message, self->install_commands);
   1671 }
   1672 
   1673 static void
   1674 rule_clean (JBResource *res)
   1675 {
   1676   JBRule *self = JB_RULE(res);
   1677   GSList *output_files;
   1678 
   1679   output_files = rule_expand_variables(self, self->output_files);
   1680 
   1681   jb_action_rm_list(output_files);
   1682 
   1683   jb_g_slist_free_deep(output_files);
   1684 }
   1685 
   1686 static void
   1687 jb_rule_init (JBRule *self)
   1688 {
   1689 }
   1690 
   1691 static void
   1692 jb_rule_class_init (JBRuleClass *class)
   1693 {
   1694   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   1695 
   1696   rclass->build = rule_build;
   1697   rclass->install = rule_install;
   1698   rclass->clean = rule_clean;
   1699 }
   1700 
   1701 G_DEFINE_ABSTRACT_TYPE(JBObject, jb_object, JB_TYPE_GROUP_RESOURCE)
   1702 
   1703 static void
   1704 object_construct (JBObject *self, const char *name)
   1705 {
   1706   self->name = g_strdup(name);
   1707 
   1708   self->compile_options = jb_compile_options_new(name);
   1709   self->install_options = jb_install_options_new();
   1710 }
   1711 
   1712 static char *
   1713 object_to_string (JBResource *res)
   1714 {
   1715   JBObject *self = JB_OBJECT(res);
   1716   JBObjectClass *class = JB_OBJECT_GET_CLASS(self);
   1717 
   1718   return g_strdup_printf("%s %s", class->type, self->name);
   1719 }
   1720 
   1721 static char *
   1722 object_get_output_file (JBObject *self)
   1723 {
   1724   JBObjectClass *class;
   1725 
   1726   g_return_val_if_fail(JB_IS_OBJECT(self), NULL);
   1727 
   1728   class = JB_OBJECT_GET_CLASS(self);
   1729 
   1730   if (class->get_output_file != NULL)
   1731     return class->get_output_file(self);
   1732   else
   1733     {
   1734       g_error("get_output_file() not implemented for object type %s", G_OBJECT_TYPE_NAME(self));
   1735       return NULL;
   1736     }
   1737 }
   1738 
   1739 static GSList *
   1740 object_get_object_files (JBObject *self)
   1741 {
   1742   GSList *list = NULL;
   1743   GSList *l;
   1744 
   1745   JB_LIST_FOREACH(l, self->resources)
   1746     list = g_slist_append(list, object_resource_get_object_file(l->data));
   1747 
   1748   return list;
   1749 }
   1750 
   1751 static GSList *
   1752 object_get_headers (JBObject *self)
   1753 {
   1754   JBGroupResource *gres = JB_GROUP_RESOURCE(self);
   1755   GSList *list = NULL;
   1756   GSList *l;
   1757 
   1758   JB_LIST_FOREACH(l, self->headers)
   1759     {
   1760       const char *header = l->data;
   1761 
   1762       list = g_slist_append(list, g_strdup_printf("%s/%s",
   1763 						  gres->group->srcdir,
   1764 						  header));
   1765     }
   1766 
   1767   return list;
   1768 }
   1769 
   1770 static void
   1771 object_pre_build (JBResource *res)
   1772 {
   1773   JBObject *self = JB_OBJECT(res);
   1774   GSList *l;
   1775 
   1776   JB_LIST_FOREACH(l, self->resources)
   1777     jb_resource_pre_build(l->data);
   1778 }
   1779 
   1780 static void
   1781 object_build (JBResource *res)
   1782 {
   1783   JBObject *self = JB_OBJECT(res);
   1784   char *outfile;
   1785   GSList *object_files_list;
   1786   GSList *l;
   1787 
   1788   JB_LIST_FOREACH(l, self->resources)
   1789     jb_resource_build(l->data);
   1790 
   1791   outfile = object_get_output_file(self);
   1792   object_files_list = object_get_object_files(self);
   1793 
   1794   if (! jb_is_uptodate_list(outfile, object_files_list))
   1795     {
   1796       JBGroupResource *gres = JB_GROUP_RESOURCE(self);
   1797       JBObjectClass *object_class;
   1798       char *object_files;
   1799 
   1800       jb_resource_message_building(res);
   1801 
   1802       object_class = JB_OBJECT_GET_CLASS(self);
   1803 
   1804       object_files = jb_string_list_join(object_files_list, " ");
   1805 
   1806       jb_mkdir(gres->group->builddir);
   1807 
   1808       jb_action_exec("$cc -o $outfile"
   1809 		     " $package-cflags $group-cflags $object-cflags $stock-cflags $cflags"
   1810 		     " $package-ldflags $group-ldflags $object-ldflags $stock-ldflags $ldflags"
   1811 		     " $object-files"
   1812 		     " $package-libs $group-libs $object-libs $stock-libs $libs",
   1813 		     "package-cflags", jb_compile_options_get_cflags(jb_compile_options),
   1814 		     "group-cflags", jb_compile_options_get_cflags(gres->group->compile_options),
   1815 		     "object-cflags", jb_compile_options_get_cflags(self->compile_options),
   1816 		     "stock-cflags", object_class->stock_cflags,
   1817 		     "package-ldflags", jb_compile_options_get_ldflags(jb_compile_options),
   1818 		     "group-ldflags", jb_compile_options_get_ldflags(gres->group->compile_options),
   1819 		     "object-ldflags", jb_compile_options_get_ldflags(self->compile_options),
   1820 		     "stock-ldflags", object_class->stock_ldflags,
   1821 		     "package-libs", jb_compile_options_get_libs(jb_compile_options),
   1822 		     "group-libs", jb_compile_options_get_libs(gres->group->compile_options),
   1823 		     "object-libs", jb_compile_options_get_libs(self->compile_options),
   1824 		     "stock-libs", object_class->stock_libs,
   1825 		     "outfile", outfile,
   1826 		     "object-files", object_files,
   1827 		     NULL);
   1828 
   1829       g_free(object_files);
   1830     }
   1831 
   1832   g_free(outfile);
   1833   jb_g_slist_free_deep(object_files_list);
   1834 }
   1835 
   1836 static void
   1837 object_makedist (JBResource *res)
   1838 {
   1839   JBObject *self = JB_OBJECT(res);
   1840   GSList *l;
   1841   GSList *headers;
   1842 
   1843   JB_LIST_FOREACH(l, self->resources)
   1844     jb_resource_makedist(l->data);
   1845 
   1846   headers = object_get_headers(self);
   1847   jb_action_add_to_dist_list(headers);
   1848   jb_g_slist_free_deep(headers);
   1849 }
   1850 
   1851 static void
   1852 object_clean (JBResource *res)
   1853 {
   1854   JBObject *self = JB_OBJECT(res);
   1855   GSList *l;
   1856   char *outfile;
   1857 
   1858   JB_LIST_FOREACH(l, self->resources)
   1859     jb_resource_clean(l->data);
   1860 
   1861   outfile = object_get_output_file(self);
   1862 
   1863   jb_action_rm(outfile);
   1864 
   1865   g_free(outfile);
   1866 }
   1867 
   1868 static void
   1869 object_distclean (JBResource *res)
   1870 {
   1871   JBObject *self = JB_OBJECT(res);
   1872   GSList *l;
   1873 
   1874   JB_LIST_FOREACH(l, self->resources)
   1875     jb_resource_distclean(l->data);
   1876 }
   1877 
   1878 static void
   1879 object_maintainerclean (JBResource *res)
   1880 {
   1881   JBObject *self = JB_OBJECT(res);
   1882   GSList *l;
   1883 
   1884   JB_LIST_FOREACH(l, self->resources)
   1885     jb_resource_maintainerclean(l->data);
   1886 }
   1887 
   1888 static void
   1889 object_add_resource (JBObject *self, JBObjectResource *res)
   1890 {
   1891   g_return_if_fail(JB_IS_OBJECT(self));
   1892   g_return_if_fail(JB_IS_OBJECT_RESOURCE(res));
   1893   g_return_if_fail(res->object == NULL);
   1894 
   1895   res->object = self;
   1896 
   1897   self->resources = g_slist_append(self->resources, res);
   1898 }
   1899 
   1900 static void
   1901 object_add_header (JBObject *self, const char *filename)
   1902 {
   1903   g_return_if_fail(JB_IS_OBJECT(self));
   1904   g_return_if_fail(filename != NULL);
   1905 
   1906   self->headers = g_slist_append(self->headers, g_strdup(filename));
   1907 }
   1908 
   1909 void
   1910 jb_object_add_source (JBObject *self, const char *name)
   1911 {
   1912   g_return_if_fail(JB_IS_OBJECT(self));
   1913   g_return_if_fail(name != NULL);
   1914 
   1915   if (strchr(name, ':') != NULL)
   1916     object_add_resource(self, JB_OBJECT_RESOURCE(jb_gob_class_new(name)));
   1917   else if (strchr(name, '.') == NULL)
   1918     {
   1919       char *cfile;
   1920       char *hfile;
   1921 
   1922       cfile = g_strdup_printf("%s.c", name);
   1923       hfile = g_strdup_printf("%s.h", name);
   1924 
   1925       object_add_resource(self, JB_OBJECT_RESOURCE(jb_source_new(cfile)));
   1926       object_add_header(self, hfile);
   1927 
   1928       g_free(cfile);
   1929       g_free(hfile);
   1930     }
   1931   else
   1932     {
   1933       if (g_str_has_suffix(name, ".c"))
   1934 	object_add_resource(self, JB_OBJECT_RESOURCE(jb_source_new(name)));
   1935       else if (g_str_has_suffix(name, ".h"))
   1936 	object_add_header(self, name);
   1937       else
   1938 	g_error("the extension of source file \"%s\" is not .c or .h", name);
   1939     }
   1940 }
   1941 
   1942 void
   1943 jb_object_add_sources (JBObject *self, const char *name, ...)
   1944 {
   1945   va_list args;
   1946 
   1947   g_return_if_fail(JB_IS_OBJECT(self));
   1948 
   1949   va_start(args, name);
   1950 
   1951   while (name != NULL)
   1952     {
   1953       jb_object_add_source(self, name);
   1954       name = va_arg(args, const char *);
   1955     }
   1956 
   1957   va_end(args);
   1958 }
   1959 
   1960 static void
   1961 jb_object_init (JBObject *self)
   1962 {
   1963 }
   1964 
   1965 static void
   1966 jb_object_class_init (JBObjectClass *class)
   1967 {
   1968   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   1969 
   1970   rclass->to_string = object_to_string;
   1971   rclass->pre_build = object_pre_build;
   1972   rclass->build = object_build;
   1973   rclass->makedist = object_makedist;
   1974   rclass->clean = object_clean;
   1975   rclass->distclean = object_distclean;
   1976   rclass->maintainerclean = object_maintainerclean;
   1977 }
   1978 
   1979 G_DEFINE_TYPE(JBProgram, jb_program, JB_TYPE_OBJECT)
   1980 
   1981 JBProgram *
   1982 jb_program_new (const char *name)
   1983 {
   1984   JBProgram *self;
   1985   JBObject *object;
   1986 
   1987   g_return_val_if_fail(name != NULL, NULL);
   1988 
   1989   self = g_object_new(JB_TYPE_PROGRAM, NULL);
   1990   object = JB_OBJECT(self);
   1991 
   1992   object_construct(object, name);
   1993   jb_install_options_set_installdir(object->install_options, "$bindir");
   1994 
   1995   return self;
   1996 }
   1997 
   1998 static void
   1999 program_install (JBResource *res)
   2000 {
   2001   JBObject *object = JB_OBJECT(res);
   2002   char *outfile;
   2003 
   2004   outfile = object_get_output_file(object);
   2005 
   2006   jb_install_options_install_program(object->install_options, outfile, NULL);
   2007 
   2008   g_free(outfile);
   2009 }
   2010 
   2011 static char *
   2012 program_get_output_file (JBObject *object)
   2013 {
   2014   JBGroupResource *gres = JB_GROUP_RESOURCE(object);
   2015 
   2016   return g_strdup_printf("%s/%s", gres->group->builddir, object->name);
   2017 }
   2018 
   2019 static void
   2020 jb_program_init (JBProgram *self)
   2021 {
   2022 }
   2023 
   2024 static void
   2025 jb_program_class_init (JBProgramClass *class)
   2026 {
   2027   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   2028   JBObjectClass *oclass = JB_OBJECT_CLASS(class);
   2029 
   2030   rclass->install = program_install;
   2031 
   2032   oclass->type = "program";
   2033   oclass->get_output_file = program_get_output_file;
   2034 }
   2035 
   2036 G_DEFINE_TYPE(JBModule, jb_module, JB_TYPE_OBJECT)
   2037 
   2038 JBModule *
   2039 jb_module_new (const char *name)
   2040 {
   2041   JBModule *self;
   2042   JBObject *object;
   2043 
   2044   g_return_val_if_fail(name != NULL, NULL);
   2045 
   2046   self = g_object_new(JB_TYPE_MODULE, NULL);
   2047   object = JB_OBJECT(self);
   2048 
   2049   object_construct(object, name);
   2050   jb_install_options_set_installdir(object->install_options, "$pkglibdir");
   2051 
   2052   return self;
   2053 }
   2054 
   2055 static void
   2056 module_install (JBResource *res)
   2057 {
   2058   JBObject *object = JB_OBJECT(res);
   2059   char *outfile;
   2060 
   2061   outfile = object_get_output_file(object);
   2062 
   2063   jb_install_options_install_library(object->install_options, outfile, NULL);
   2064 
   2065   g_free(outfile);
   2066 }
   2067 
   2068 static char *
   2069 module_get_output_file (JBObject *object)
   2070 {
   2071   JBGroupResource *gres = JB_GROUP_RESOURCE(object);
   2072 
   2073   return g_strdup_printf("%s/%s.so", gres->group->builddir, object->name);
   2074 }
   2075 
   2076 static void
   2077 jb_module_init (JBModule *self)
   2078 {
   2079 }
   2080 
   2081 static void
   2082 jb_module_class_init (JBModuleClass *class)
   2083 {
   2084   JBResourceClass *rclass = JB_RESOURCE_CLASS(class);
   2085   JBObjectClass *oclass = JB_OBJECT_CLASS(class);
   2086 
   2087   rclass->install = module_install;
   2088 
   2089   oclass->type = "module";
   2090   oclass->stock_cflags = "-fPIC";
   2091   oclass->stock_cppflags = "-DPIC";
   2092   oclass->stock_ldflags = "-shared";
   2093   oclass->get_output_file = module_get_output_file;
   2094 }