github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/make/tools/atree/atree.cpp (about)

     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  // Escape the filename so that it can be added to the makefile properly.
    94  static string
    95  escape_filename(const string& name)
    96  {
    97      ostringstream new_name;
    98      for (string::const_iterator iter = name.begin(); iter != name.end(); ++iter)
    99      {
   100          switch (*iter)
   101          {
   102              case '$':
   103                  new_name << "$$";
   104                  break;
   105              default:
   106                  new_name << *iter;
   107                  break;
   108          }
   109      }
   110      return new_name.str();
   111  }
   112  
   113  int
   114  main(int argc, char* const* argv)
   115  {
   116      int err;
   117      bool done = false;
   118      while (!done) {
   119          int opt = getopt(argc, argv, "f:I:o:hlm:v:d");
   120          switch (opt)
   121          {
   122              case -1:
   123                  done = true;
   124                  break;
   125              case 'f':
   126                  g_listFiles.push_back(string(optarg));
   127                  break;
   128              case 'I':
   129                  g_inputBases.push_back(string(optarg));
   130                  break;
   131              case 'o':
   132                  if (g_outputBase.length() != 0) {
   133                      fprintf(stderr, "%s: -o may only be supplied once -- "
   134                                  "-o %s\n", argv[0], optarg);
   135                      return usage();
   136                  }
   137                  g_outputBase = optarg;
   138                  break;
   139              case 'l':
   140                  g_useHardLinks = true;
   141                  break;
   142              case 'm':
   143                  if (g_dependency.length() != 0) {
   144                      fprintf(stderr, "%s: -m may only be supplied once -- "
   145                                  "-m %s\n", argv[0], optarg);
   146                      return usage();
   147                  }
   148                  g_dependency = optarg;
   149                  break;
   150              case 'v':
   151                  if (!add_variable(optarg)) {
   152                      fprintf(stderr, "%s Invalid expression in '-v %s': "
   153                              "expected format is '-v VAR=VALUE'.\n",
   154                              argv[0], optarg);
   155                      return usage();
   156                  }
   157                  break;
   158              case 'd':
   159                  g_debug = true;
   160                  break;
   161              default:
   162              case '?':
   163              case 'h':
   164                  return usage();
   165          }
   166      }
   167      if (optind != argc) {
   168          fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]);
   169          return usage();
   170      }
   171  
   172      if (g_listFiles.size() == 0) {
   173          fprintf(stderr, "%s: At least one -f option must be supplied.\n",
   174                   argv[0]);
   175          return usage();
   176      }
   177  
   178      if (g_inputBases.size() == 0) {
   179          fprintf(stderr, "%s: At least one -I option must be supplied.\n",
   180                   argv[0]);
   181          return usage();
   182      }
   183  
   184      if (g_outputBase.length() == 0) {
   185          fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]);
   186          return usage();
   187      }
   188  
   189  
   190  #if 0
   191      for (vector<string>::iterator it=g_listFiles.begin();
   192                                  it!=g_listFiles.end(); it++) {
   193          printf("-f \"%s\"\n", it->c_str());
   194      }
   195      for (vector<string>::iterator it=g_inputBases.begin();
   196                                  it!=g_inputBases.end(); it++) {
   197          printf("-I \"%s\"\n", it->c_str());
   198      }
   199      printf("-o \"%s\"\n", g_outputBase.c_str());
   200      if (g_useHardLinks) {
   201          printf("-l\n");
   202      }
   203  #endif
   204  
   205      vector<FileRecord> files;
   206      vector<FileRecord> more;
   207      vector<string> excludes;
   208      set<string> directories;
   209      set<string> deleted;
   210  
   211      // read file lists
   212      for (vector<string>::iterator it=g_listFiles.begin();
   213                                   it!=g_listFiles.end(); it++) {
   214          err = read_list_file(*it, g_variables, &files, &excludes);
   215          if (err != 0) {
   216              return err;
   217          }
   218      }
   219  
   220      // look for input files
   221      err = 0;
   222      for (vector<FileRecord>::iterator it=files.begin();
   223                                  it!=files.end(); it++) {
   224          err |= locate(&(*it), g_inputBases);
   225      }
   226  
   227      // expand the directories that we should copy into a list of files
   228      for (vector<FileRecord>::iterator it=files.begin();
   229                                  it!=files.end(); it++) {
   230          if (it->sourceIsDir) {
   231              err |= list_dir(*it, excludes, &more);
   232          }
   233      }
   234      for (vector<FileRecord>::iterator it=more.begin();
   235                                  it!=more.end(); it++) {
   236          files.push_back(*it);
   237      }
   238  
   239      // get the name and modtime of the output files
   240      for (vector<FileRecord>::iterator it=files.begin();
   241                                  it!=files.end(); it++) {
   242          stat_out(g_outputBase, &(*it));
   243      }
   244  
   245      if (err != 0) {
   246          return 1;
   247      }
   248  
   249      // gather directories
   250      for (vector<FileRecord>::iterator it=files.begin();
   251                                  it!=files.end(); it++) {
   252          if (it->sourceIsDir) {
   253              directories.insert(it->outPath);
   254          } else {
   255              string s = dir_part(it->outPath);
   256              if (s != ".") {
   257                  directories.insert(s);
   258              }
   259          }
   260      }
   261  
   262      // gather files that should become directores
   263      // and directories that should become files
   264      for (vector<FileRecord>::iterator it=files.begin();
   265                                  it!=files.end(); it++) {
   266          if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
   267              deleted.insert(it->outPath);
   268          }
   269      }
   270  
   271      // delete files
   272      for (set<string>::iterator it=deleted.begin();
   273                                  it!=deleted.end(); it++) {
   274          debug_printf("deleting %s\n", it->c_str());
   275          err = remove_recursively(*it);
   276          if (err != 0) {
   277              return err;
   278          }
   279      }
   280  
   281      // remove all files or directories as requested from the input atree file.
   282      // must be done before create new directories.
   283      for (vector<FileRecord>::iterator it=files.begin();
   284                                  it!=files.end(); it++) {
   285          if (!it->sourceIsDir) {
   286              if (it->fileOp == FILE_OP_REMOVE &&
   287                      deleted.count(it->outPath) == 0) {
   288                  debug_printf("remove %s\n", it->outPath.c_str());
   289                  err = remove_recursively(it->outPath);
   290                  if (err != 0) {
   291                      return err;
   292                  }
   293              }
   294          }
   295      }
   296  
   297      // make directories
   298      for (set<string>::iterator it=directories.begin();
   299                                  it!=directories.end(); it++) {
   300          debug_printf("mkdir %s\n", it->c_str());
   301          err = mkdir_recursively(*it);
   302          if (err != 0) {
   303              return err;
   304          }
   305      }
   306  
   307      // copy (or link) files that are newer or of different size
   308      for (vector<FileRecord>::iterator it=files.begin();
   309                                  it!=files.end(); it++) {
   310          if (!it->sourceIsDir) {
   311              if (it->fileOp == FILE_OP_REMOVE) {
   312                  continue;
   313              }
   314  
   315              debug_printf("copy %s(%ld) ==> %s(%ld)",
   316                  it->sourcePath.c_str(), it->sourceMod,
   317                  it->outPath.c_str(), it->outMod);
   318  
   319              if (it->outSize != it->sourceSize || it->outMod < it->sourceMod) {
   320                  err = copy_file(it->sourcePath, it->outPath);
   321                  debug_printf(" done.\n");
   322                  if (err != 0) {
   323                      return err;
   324                  }
   325              } else {
   326                  debug_printf(" skipping.\n");
   327              }
   328  
   329              if (it->fileOp == FILE_OP_STRIP) {
   330                  debug_printf("strip %s\n", it->outPath.c_str());
   331                  err = strip_file(it->outPath);
   332                  if (err != 0) {
   333                      return err;
   334                  }
   335              }
   336          }
   337      }
   338  
   339      // output the dependency file
   340      if (g_dependency.length() != 0) {
   341          FILE *f = fopen(g_dependency.c_str(), "w");
   342          if (f != NULL) {
   343              fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n");
   344              for (vector<FileRecord>::iterator it=files.begin();
   345                                  it!=files.end(); it++) {
   346                  if (!it->sourceIsDir) {
   347                      fprintf(f, "%s \\\n",
   348                              escape_filename(it->sourcePath).c_str());
   349                  }
   350              }
   351              fprintf(f, "\n");
   352              fclose(f);
   353          } else {
   354              fprintf(stderr, "error opening manifest file for write: %s\n",
   355                      g_dependency.c_str());
   356          }
   357      }
   358  
   359      return 0;
   360  }