Home | History | Annotate | Download | only in atree
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <unistd.h>
      5 #include <stdarg.h>
      6 #include "options.h"
      7 #include "files.h"
      8 #include "fs.h"
      9 #include <set>
     10 #include <iostream>
     11 #include <sstream>
     12 
     13 using namespace std;
     14 
     15 bool g_debug = getenv("ATREE_DEBUG") != NULL;
     16 vector<string> g_listFiles;
     17 vector<string> g_inputBases;
     18 map<string, string> g_variables;
     19 string g_outputBase;
     20 string g_dependency;
     21 bool g_useHardLinks = false;
     22 
     23 const char* USAGE =
     24 "\n"
     25 "Usage: atree OPTIONS\n"
     26 "\n"
     27 "Options:\n"
     28 "  -f FILELIST    Specify one or more files containing the\n"
     29 "                 list of files to copy.\n"
     30 "  -I INPUTDIR    Specify one or more base directories in\n"
     31 "                 which to look for the files\n"
     32 "  -o OUTPUTDIR   Specify the directory to copy all of the\n"
     33 "                 output files to.\n"
     34 "  -l             Use hard links instead of copying the files.\n"
     35 "  -m DEPENDENCY  Output a make-formatted file containing the list.\n"
     36 "                 of files included.  It sets the variable ATREE_FILES.\n"
     37 "  -v VAR=VAL     Replaces ${VAR} by VAL when reading input files.\n"
     38 "  -d             Verbose debug mode.\n"
     39 "\n"
     40 "FILELIST file format:\n"
     41 "  The FILELIST files contain the list of files that will end up\n"
     42 "  in the final OUTPUTDIR.  Atree will look for files in the INPUTDIR\n"
     43 "  directories in the order they are specified.\n"
     44 "\n"
     45 "  In a FILELIST file, comment lines start with a #.  Other lines\n"
     46 "  are of the format:\n"
     47 "\n"
     48 "    [rm|strip] DEST\n"
     49 "    SRC [strip] DEST\n"
     50 "    -SRCPATTERN\n"
     51 "\n"
     52 "  DEST should be path relative to the output directory.\n"
     53 "  'rm DEST' removes the destination file and fails if it's missing.\n"
     54 "  'strip DEST' strips the binary destination file.\n"
     55 "  If SRC is supplied, the file names can be different.\n"
     56 "  SRCPATTERN is a pattern for the filenames.\n"
     57 "\n";
     58 
     59 int usage()
     60 {
     61     fwrite(USAGE, strlen(USAGE), 1, stderr);
     62     return 1;
     63 }
     64 
     65 static bool
     66 add_variable(const char* arg) {
     67     const char* p = arg;
     68     while (*p && *p != '=') p++;
     69 
     70     if (*p == 0 || p == arg || p[1] == 0) {
     71         return false;
     72     }
     73 
     74     ostringstream var;
     75     var << "${" << string(arg, p-arg) << "}";
     76     g_variables[var.str()] = string(p+1);
     77     return true;
     78 }
     79 
     80 static void
     81 debug_printf(const char* format, ...)
     82 {
     83     if (g_debug) {
     84         fflush(stderr);
     85         va_list ap;
     86         va_start(ap, format);
     87         vprintf(format, ap);
     88         va_end(ap);
     89         fflush(stdout);
     90     }
     91 }
     92 
     93 int
     94 main(int argc, char* const* argv)
     95 {
     96     int err;
     97     bool done = false;
     98     while (!done) {
     99         int opt = getopt(argc, argv, "f:I:o:hlm:v:d");
    100         switch (opt)
    101         {
    102             case -1:
    103                 done = true;
    104                 break;
    105             case 'f':
    106                 g_listFiles.push_back(string(optarg));
    107                 break;
    108             case 'I':
    109                 g_inputBases.push_back(string(optarg));
    110                 break;
    111             case 'o':
    112                 if (g_outputBase.length() != 0) {
    113                     fprintf(stderr, "%s: -o may only be supplied once -- "
    114                                 "-o %s\n", argv[0], optarg);
    115                     return usage();
    116                 }
    117                 g_outputBase = optarg;
    118                 break;
    119             case 'l':
    120                 g_useHardLinks = true;
    121                 break;
    122             case 'm':
    123                 if (g_dependency.length() != 0) {
    124                     fprintf(stderr, "%s: -m may only be supplied once -- "
    125                                 "-m %s\n", argv[0], optarg);
    126                     return usage();
    127                 }
    128                 g_dependency = optarg;
    129                 break;
    130             case 'v':
    131                 if (!add_variable(optarg)) {
    132                     fprintf(stderr, "%s Invalid expression in '-v %s': "
    133                             "expected format is '-v VAR=VALUE'.\n",
    134                             argv[0], optarg);
    135                     return usage();
    136                 }
    137                 break;
    138             case 'd':
    139                 g_debug = true;
    140                 break;
    141             default:
    142             case '?':
    143             case 'h':
    144                 return usage();
    145         }
    146     }
    147     if (optind != argc) {
    148         fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]);
    149         return usage();
    150     }
    151 
    152     if (g_listFiles.size() == 0) {
    153         fprintf(stderr, "%s: At least one -f option must be supplied.\n",
    154                  argv[0]);
    155         return usage();
    156     }
    157 
    158     if (g_inputBases.size() == 0) {
    159         fprintf(stderr, "%s: At least one -I option must be supplied.\n",
    160                  argv[0]);
    161         return usage();
    162     }
    163 
    164     if (g_outputBase.length() == 0) {
    165         fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]);
    166         return usage();
    167     }
    168 
    169 
    170 #if 0
    171     for (vector<string>::iterator it=g_listFiles.begin();
    172                                 it!=g_listFiles.end(); it++) {
    173         printf("-f \"%s\"\n", it->c_str());
    174     }
    175     for (vector<string>::iterator it=g_inputBases.begin();
    176                                 it!=g_inputBases.end(); it++) {
    177         printf("-I \"%s\"\n", it->c_str());
    178     }
    179     printf("-o \"%s\"\n", g_outputBase.c_str());
    180     if (g_useHardLinks) {
    181         printf("-l\n");
    182     }
    183 #endif
    184 
    185     vector<FileRecord> files;
    186     vector<FileRecord> more;
    187     vector<string> excludes;
    188     set<string> directories;
    189     set<string> deleted;
    190 
    191     // read file lists
    192     for (vector<string>::iterator it=g_listFiles.begin();
    193                                  it!=g_listFiles.end(); it++) {
    194         err = read_list_file(*it, g_variables, &files, &excludes);
    195         if (err != 0) {
    196             return err;
    197         }
    198     }
    199 
    200     // look for input files
    201     err = 0;
    202     for (vector<FileRecord>::iterator it=files.begin();
    203                                 it!=files.end(); it++) {
    204         err |= locate(&(*it), g_inputBases);
    205     }
    206 
    207     // expand the directories that we should copy into a list of files
    208     for (vector<FileRecord>::iterator it=files.begin();
    209                                 it!=files.end(); it++) {
    210         if (it->sourceIsDir) {
    211             err |= list_dir(*it, excludes, &more);
    212         }
    213     }
    214     for (vector<FileRecord>::iterator it=more.begin();
    215                                 it!=more.end(); it++) {
    216         files.push_back(*it);
    217     }
    218 
    219     // get the name and modtime of the output files
    220     for (vector<FileRecord>::iterator it=files.begin();
    221                                 it!=files.end(); it++) {
    222         stat_out(g_outputBase, &(*it));
    223     }
    224 
    225     if (err != 0) {
    226         return 1;
    227     }
    228 
    229     // gather directories
    230     for (vector<FileRecord>::iterator it=files.begin();
    231                                 it!=files.end(); it++) {
    232         if (it->sourceIsDir) {
    233             directories.insert(it->outPath);
    234         } else {
    235             string s = dir_part(it->outPath);
    236             if (s != ".") {
    237                 directories.insert(s);
    238             }
    239         }
    240     }
    241 
    242     // gather files that should become directores
    243     // and directories that should become files
    244     for (vector<FileRecord>::iterator it=files.begin();
    245                                 it!=files.end(); it++) {
    246         if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
    247             deleted.insert(it->outPath);
    248         }
    249     }
    250 
    251     // delete files
    252     for (set<string>::iterator it=deleted.begin();
    253                                 it!=deleted.end(); it++) {
    254         debug_printf("deleting %s\n", it->c_str());
    255         err = remove_recursively(*it);
    256         if (err != 0) {
    257             return err;
    258         }
    259     }
    260 
    261     // remove all files or directories as requested from the input atree file.
    262     // must be done before create new directories.
    263     for (vector<FileRecord>::iterator it=files.begin();
    264                                 it!=files.end(); it++) {
    265         if (!it->sourceIsDir) {
    266             if (it->fileOp == FILE_OP_REMOVE &&
    267                     deleted.count(it->outPath) == 0) {
    268                 debug_printf("remove %s\n", it->outPath.c_str());
    269                 err = remove_recursively(it->outPath);
    270                 if (err != 0) {
    271                     return err;
    272                 }
    273             }
    274         }
    275     }
    276 
    277     // make directories
    278     for (set<string>::iterator it=directories.begin();
    279                                 it!=directories.end(); it++) {
    280         debug_printf("mkdir %s\n", it->c_str());
    281         err = mkdir_recursively(*it);
    282         if (err != 0) {
    283             return err;
    284         }
    285     }
    286 
    287     // copy (or link) files that are newer or of different size
    288     for (vector<FileRecord>::iterator it=files.begin();
    289                                 it!=files.end(); it++) {
    290         if (!it->sourceIsDir) {
    291             if (it->fileOp == FILE_OP_REMOVE) {
    292                 continue;
    293             }
    294 
    295             debug_printf("copy %s(%ld) ==> %s(%ld)",
    296                 it->sourcePath.c_str(), it->sourceMod,
    297                 it->outPath.c_str(), it->outMod);
    298 
    299             if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) {
    300                 err = copy_file(it->sourcePath, it->outPath);
    301                 debug_printf(" done.\n");
    302                 if (err != 0) {
    303                     return err;
    304                 }
    305             } else {
    306                 debug_printf(" skipping.\n");
    307             }
    308 
    309             if (it->fileOp == FILE_OP_STRIP) {
    310                 debug_printf("strip %s\n", it->outPath.c_str());
    311                 err = strip_file(it->outPath);
    312                 if (err != 0) {
    313                     return err;
    314                 }
    315             }
    316         }
    317     }
    318 
    319     // output the dependency file
    320     if (g_dependency.length() != 0) {
    321         FILE *f = fopen(g_dependency.c_str(), "w");
    322         if (f != NULL) {
    323             fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n");
    324             for (vector<FileRecord>::iterator it=files.begin();
    325                                 it!=files.end(); it++) {
    326                 if (!it->sourceIsDir) {
    327                     fprintf(f, "%s \\\n", it->sourcePath.c_str());
    328                 }
    329             }
    330             fprintf(f, "\n");
    331             fclose(f);
    332         } else {
    333             fprintf(stderr, "error opening manifest file for write: %s\n",
    334                     g_dependency.c_str());
    335         }
    336     }
    337 
    338     return 0;
    339 }
    340