Refactor the statistics gathering code in run.ml
[tatoo.git] / remake.cpp
index b8387be..de68c4d 100644 (file)
@@ -343,7 +343,7 @@ https://github.com/apenwarr/redo for an implementation and some comprehensive do
 \section sec-licensing Licensing
 
 @author Guillaume Melquiond
-@version 0.9
+@version 0.10
 @date 2012-2013
 @copyright
 This program is free software: you can redistribute it and/or modify
@@ -373,8 +373,8 @@ When building a target, the following sequence of events happens:
 
 - #start calls #find_rule (and #find_generic_rule) to get the rule.
 - It then creates a pseudo-client if the rule has static dependencies, or
-  calls #run_script otherwise. In both cases, a new job is created and its
-  targets are put into #job_targets.
+  calls #run_script otherwise. In both cases, a new job is created; the
+  rule and the variables are stored into #jobs.
 - #run_script creates a shell process and stores it in #job_pids. It
   increases #running_jobs.
 - The child process possibly calls <b>remake</b> with a list of targets.
@@ -392,7 +392,7 @@ When building a target, the following sequence of events happens:
 - When a child process ends, #server_loop calls #finalize_job, which
   removes the process from #job_pids, decreases #running_jobs, and calls
   #complete_job.
-- #complete_job removes the job from #job_targets and calls #update_status
+- #complete_job removes the job from #jobs and calls #update_status
   to change the status of the targets. It also removes the target files in
   case of failure.
 */
@@ -668,7 +668,7 @@ static int waiting_jobs = 0;
 
 /**
  * Global counter used to produce increasing job numbers.
- * @see job_targets
+ * @see jobs
  */
 static int job_counter = 0;
 
@@ -719,6 +719,11 @@ static std::string working_dir;
  */
 static std::string prefix_dir;
 
+/**
+ * Whether the prefix directory is different from #working_dir.
+ */
+static bool changed_prefix_dir;
+
 /**
  * Whether target-specific variables are propagated to prerequisites.
  */
@@ -870,7 +875,16 @@ static void init_prefix_dir()
                struct stat s;
                if (stat((prefix_dir + "/Remakefile").c_str(), &s) == 0)
                {
-                       chdir(prefix_dir.c_str());
+                       if (!changed_prefix_dir) return;
+                       if (chdir(prefix_dir.c_str()))
+                       {
+                               perror("Failed to change working directory");
+                               exit(EXIT_FAILURE);
+                       }
+                       if (show_targets)
+                       {
+                               std::cout << "remake: Entering directory `" << prefix_dir << '\'' << std::endl;
+                       }
                        return;
                }
                size_t pos = prefix_dir.find_last_of('/');
@@ -880,6 +894,7 @@ static void init_prefix_dir()
                        exit(EXIT_FAILURE);
                }
                prefix_dir.erase(pos);
+               changed_prefix_dir = true;
        }
 }
 
@@ -1076,44 +1091,44 @@ static int expect_token(std::istream &in, int mask)
 /**
  * Read a (possibly quoted) word.
  */
-static std::string read_word(std::istream &in)
+static std::string read_word(std::istream &in, bool detect_equal = true)
 {
-       int c = in.get();
+       int c = in.peek();
        std::string res;
        if (!in.good()) return res;
-       char const *separators = " \t\r\n:$(),=+\"";
+       char const *separators = " \t\r\n$(),:";
        bool quoted = c == '"';
-       if (!quoted)
-       {
-               if (strchr(separators, c))
-               {
-                       in.putback(c);
-                       return res;
-               }
-               res += c;
-       }
+       if (quoted) in.ignore(1);
+       bool plus = false;
        while (true)
        {
-               c = in.get();
+               c = in.peek();
                if (!in.good()) return res;
                if (quoted)
                {
+                       in.ignore(1);
                        if (c == '\\')
                                res += in.get();
                        else if (c == '"')
-                               return res;
+                               quoted = false;
                        else
                                res += c;
+                       continue;
                }
-               else
+               if (detect_equal && c == '=')
                {
-                       if (strchr(separators, c))
-                       {
-                               in.putback(c);
-                               return res;
-                       }
-                       res += c;
+                       if (plus) in.putback('+');
+                       return res;
                }
+               if (plus)
+               {
+                       res += '+';
+                       plus = false;
+               }
+               if (strchr(separators, c)) return res;
+               in.ignore(1);
+               if (detect_equal && c == '+') plus = true;
+               else res += c;
        }
 }
 
@@ -1218,11 +1233,11 @@ input_status input_generator::next(std::string &res)
        switch (expect_token(in, Word | Dollarpar))
        {
        case Word:
-               res = read_word(in);
+               res = read_word(in, false);
                return Success;
        case Dollarpar:
        {
-               std::string name = read_word(in);
+               std::string name = read_word(in, false);
                if (name.empty()) return SyntaxError;
                if (expect_token(in, Rightpar))
                        nested = new variable_generator(name, local_variables);
@@ -2021,21 +2036,23 @@ static bool still_need_rebuild(std::string const &target)
  */
 static void complete_job(int job_id, bool success)
 {
-       DEBUG_open << "Completing job " << job_id << "... ";
+       DEBUG << "Completing job " << job_id << '\n';
        job_map::iterator i = jobs.find(job_id);
        assert(i != jobs.end());
        string_list const &targets = i->second.rule.targets;
        if (success)
        {
+               if (show_targets) std::cout << "Finished";
                for (string_list::const_iterator j = targets.begin(),
                     j_end = targets.end(); j != j_end; ++j)
                {
                        update_status(*j);
+                       if (show_targets) std::cout << ' ' << *j;
                }
+               if (show_targets) std::cout << std::endl;
        }
        else
        {
-               DEBUG_close << "failed\n";
                std::cerr << "Failed to build";
                for (string_list::const_iterator j = targets.begin(),
                     j_end = targets.end(); j != j_end; ++j)
@@ -2137,25 +2154,17 @@ static std::string prepare_script(job_t const &job)
  */
 static status_e run_script(int job_id, job_t const &job)
 {
-       if (show_targets)
-       {
-               std::cout << "Building";
-               for (string_list::const_iterator i = job.rule.targets.begin(),
-                    i_end = job.rule.targets.end(); i != i_end; ++i)
-               {
-                       std::cout << ' ' << *i;
-               }
-               std::cout << std::endl;
-       }
-
        ref_ptr<dependency_t> dep;
        dep->targets = job.rule.targets;
        dep->deps.insert(job.rule.deps.begin(), job.rule.deps.end());
+       if (show_targets) std::cout << "Building";
        for (string_list::const_iterator i = job.rule.targets.begin(),
             i_end = job.rule.targets.end(); i != i_end; ++i)
        {
                dependencies[*i] = dep;
+               if (show_targets) std::cout << ' ' << *i;
        }
+       if (show_targets) std::cout << std::endl;
 
        std::string script = prepare_script(job);
 
@@ -2792,6 +2801,10 @@ static void server_mode(std::string const &remakefile, string_list const &target
        free(socket_name);
 #endif
        save_dependencies();
+       if (show_targets && changed_prefix_dir)
+       {
+               std::cout << "remake: Leaving directory `" << prefix_dir << '\'' << std::endl;
+       }
        exit(build_failure ? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
@@ -2927,8 +2940,6 @@ static void usage(int exit_status)
  */
 int main(int argc, char *argv[])
 {
-       init_working_dir();
-
        std::string remakefile;
        string_list targets;
        bool literal_targets = false;
@@ -2973,11 +2984,14 @@ int main(int argc, char *argv[])
                                continue;
                        }
                        new_target:
-                       targets.push_back(normalize(arg, working_dir, working_dir));
+                       targets.push_back(arg);
                        DEBUG << "New target: " << arg << '\n';
                }
        }
 
+       init_working_dir();
+       normalize_list(targets, working_dir, working_dir);
+
        if (indirect_targets)
        {
                load_dependencies(std::cin);