<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <list>
#include <ostream>
#include <vector>

#include <process/clock.hpp>
#include <process/collect.hpp>
#include <process/defer.hpp>
#include <process/io.hpp>
#include <process/process.hpp>
#include <process/subprocess.hpp>

#include <stout/strings.hpp>

#include "linux/perf.hpp"

using std::list;
using std::ostringstream;
using std::set;
using std::string;
using std::vector;

using namespace process;

namespace perf {

// Delimiter for fields in perf stat output.
const string PERF_DELIMITER = ",";

// Use an empty string as the key for the parse output when sampling a
// set of pids. No valid cgroup can be an empty string.
const string PIDS_KEY = "";

namespace internal {

string command(
    const set<string>& events,
    const set<string>& cgroups,
    const Duration& duration)
{
  ostringstream command;

  command << "perf stat -x" << PERF_DELIMITER << " -a";
  command << " --log-fd 1";  // Ensure all output goes to stdout.
  // Nested loop to produce all pairings of event and cgroup.
  foreach (const string& event, events) {
    foreach (const string& cgroup, cgroups) {
      command << " --event " << event
              << " --cgroup " << cgroup;
    }
  }
  command << " -- sleep " << stringify(duration.secs());

  return command.str();
}

string command(
    const set<string>& events,
    const string& cgroup,
    const Duration& duration)
{
  set<string> cgroups;
  cgroups.insert(cgroup);

  return command(events, cgroups, duration);
}

string command(
    const set<string>& events,
    const set<pid_t>& pids,
    const Duration& duration)
{
  ostringstream command;

  command << "perf stat -x" << PERF_DELIMITER << " -a";
  command << " --log-fd 1";  // Ensure all output goes to stdout.
  command << " --event " << strings::join(",", events);
  command << " --pid " << strings::join(",", pids);
  command << " -- sleep " << stringify(duration.secs());

  return command.str();
}

// Normalize a perf event name. After normalization the event name
// should match an event field in the PerfStatistics protobuf.
inline string normalize(const string& s)
{
  string lower = strings::lower(s);
  return strings::replace(lower, "-", "_");
}

class PerfSampler : public Process<PerfSampler>
{
public:
  PerfSampler(const string& _command, const Duration& _duration)
    : command(_command), duration(_duration) {}

  virtual ~PerfSampler() {}

  Future<hashmap<string, mesos::PerfStatistics>> future()
  {
    return promise.future();
  }

protected:
  virtual void initialize()
  {
    // Stop when no one cares.
    promise.future().onDiscard(lambda::bind(
          static_cast<void(*)(const UPID&, bool)>(terminate), self(), true));

    if (duration < Seconds(0)) {
      promise.fail("Perf sample duration cannot be negative: '" +
                    stringify(duration.secs()) + "'");
      terminate(self());
      return;
    }

    start = Clock::now();

    sample();
  }

  virtual void finalize()
  {
    discard(output);

    // Kill the perf process if it's still running.
    if (perf.isSome() && perf.get().status().isPending()) {
      kill(perf.get().pid(), SIGKILL);
    }

    promise.discard();
  }

private:
  void sample()
  {
    Try<Subprocess> _perf = subprocess(
        command,
        Subprocess::PIPE(),
        Subprocess::PIPE(),
        Subprocess::PIPE());

    if (_perf.isError()) {
      promise.fail("Failed to launch perf process: " + _perf.error());
      terminate(self());
      return;
    }
    perf = _perf.get();

    // Start reading from stdout and stderr now. We don't use stderr
    // but must read from it to avoid the subprocess blocking on the
    // pipe.
    output.push_back(process::io::read(perf.get().out().get()));
    output.push_back(process::io::read(perf.get().err().get()));

    // Wait for the process to exit.
    perf.get().status()
      .onAny(defer(self(), &Self::_sample, lambda::_1));
  }

  void _sample(const Future<Option<int>>& status)
  {
    if (!status.isReady()) {
      promise.fail("Failed to get exit status of perf process: " +
                   (status.isFailed() ? status.failure() : "discarded"));
      terminate(self());
      return;
    }

    if (status.get().get() != 0) {
      promise.fail("Failed to execute perf, exit status: " +
                    stringify(WEXITSTATUS(status.get().get())));

      terminate(self());
      return;
    }

    // Wait until we collect all output.
    collect(output).onAny(defer(self(), &Self::__sample, lambda::_1));
  }

  void  __sample(const Future<list<string>>& future)
  {
    if (!future.isReady()) {
      promise.fail("Failed to collect output of perf process: " +
                   (future.isFailed() ? future.failure() : "discarded"));
      terminate(self());
      return;
    }

    // Parse output from stdout.
    Try<hashmap<string, mesos::PerfStatistics>> parse =
      perf::parse(output.front().get());
    if (parse.isError()) {
      promise.fail("Failed to parse perf output: " + parse.error());
      terminate(self());
      return;
    }

    // Create a non-const copy from the Try<> so we can set the
    // timestamp and duration.
    hashmap<string, mesos::PerfStatistics> statistics = parse.get();
    foreachvalue (mesos::PerfStatistics& s, statistics) {
      s.set_timestamp(start.secs());
      s.set_duration(duration.secs());
    }

    promise.set(statistics);
    terminate(self());
    return;
  }

  const string command;
  const Duration duration;
  Time start;
  Option<Subprocess> perf;
  Promise<hashmap<string, mesos::PerfStatistics>> promise;
  list<Future<string>> output;
};

// Helper to select a single key from the hashmap of perf statistics.
Future<mesos::PerfStatistics> select(
    const string& key,
    const hashmap<string, mesos::PerfStatistics>& statistics)
{
  return statistics.get(key).get();
}

} // namespace internal {

Future<mesos::PerfStatistics> sample(
    const set<string>& events,
    pid_t pid,
    const Duration& duration)
{
  set<pid_t> pids;
  pids.insert(pid);
  return sample(events, pids, duration);
}

Future<mesos::PerfStatistics> sample(
    const set<string>& events,
    const set<pid_t>& pids,
    const Duration& duration)
{
  if (!supported()) {
    return Failure("Perf is not supported");
  }

  const string command = internal::command(events, pids, duration);
  internal::PerfSampler* sampler = new internal::PerfSampler(command, duration);
  Future<hashmap<string, mesos::PerfStatistics>> future = sampler->future();
  spawn(sampler, true);
  return future
    .then(lambda::bind(&internal::select, PIDS_KEY, lambda::_1));
}

Future<mesos::PerfStatistics> sample(
    const set<string>& events,
    const string& cgroup,
    const Duration& duration)
{
  set<string> cgroups;
  cgroups.insert(cgroup);
  return sample(events, cgroups, duration)
    .then(lambda::bind(&internal::select, cgroup, lambda::_1));
}

Future<hashmap<string, mesos::PerfStatistics>> sample(
    const set<string>& events,
    const set<string>& cgroups,
    const Duration& duration)
{
  if (!supported()) {
    return Failure("Perf is not supported");
  }

  const string command = internal::command(events, cgroups, duration);
  internal::PerfSampler* sampler = new internal::PerfSampler(command, duration);
  Future<hashmap<string, mesos::PerfStatistics>> future = sampler->future();
  spawn(sampler, true);
  return future;
}

bool valid(const set<string>& events)
{
  ostringstream command;

  // Log everything to stderr which is then redirected to /dev/null.
  command << "perf stat --log-fd 2";
  foreach (const string& event, events) {
    command << " --event " << event;
  }
  command << " true 2>/dev/null";

  return (os::system(command.str()) == 0);
}

bool supported()
{
  // Require Linux kernel version >= 2.6.38 for "-x" and >= 2.6.39 for
  // "--cgroup"
  Try<Version> release = os::release();

  // This is not expected to ever be an Error.
  CHECK_SOME(release);

  return release.get() >= Version(2, 6, 39);
}

Try<hashmap<string, mesos::PerfStatistics>> parse(const string& output)
{
  hashmap<string, mesos::PerfStatistics> statistics;

  foreach (const string& line, strings::tokenize(output, "\n")) {
    vector<string> tokens = strings::tokenize(line, PERF_DELIMITER);
    // Expected format for an output line is either:
    // value,event          (when sampling pids)
    // value,event,cgroup   (when sampling a cgroup)
    // assuming PERF_DELIMITER = ",".
    if (tokens.size() < 2 || tokens.size() > 3) {
      return Error("Unexpected perf output at line: " + line);
    }

    const string value = tokens[0];
    const string event = internal::normalize(tokens[1]);
    // Use the special PIDS_KEY when sampling pids.
    const string cgroup = (tokens.size() == 3 ? tokens[2] : PIDS_KEY);

    if (!statistics.contains(cgroup)) {
      statistics.put(cgroup, mesos::PerfStatistics());
    }

    const google::protobuf::Reflection* reflection =
      statistics[cgroup].GetReflection();
    const google::protobuf::FieldDescriptor* field =
      statistics[cgroup].GetDescriptor()->FindFieldByName(event);
    if (!field) {
      return Error("Unexpected perf output at line: " + line);
    }

    if (value == "<not supported>") {
      LOG(WARNING) << "Unsupported perf counter, ignoring: " << line;
      continue;
    }

    switch (field->type()) {
      case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
        {
          Try<double> number =
            (value == "<not counted>") ?  0 : numify<double>(value);

          if (number.isError()) {
            return Error("Unable to parse perf value at line: " + line);
          }

          reflection->SetDouble(&(statistics[cgroup]), field, number.get());
          break;
        }
      case google::protobuf::FieldDescriptor::TYPE_UINT64:
        {
          Try<uint64_t> number =
            (value == "<not counted>") ?  0 : numify<uint64_t>(value);

          if (number.isError()) {
            return Error("Unable to parse perf value at line: " + line);
          }

          reflection->SetUInt64(&(statistics[cgroup]), field, number.get());
          break;
        }
      default:
        return Error("Unsupported perf field type at line: " + line);
      }
  }

  return statistics;
}

} // namespace perf {
<commit_msg>Used the argv version of subprocess for linux perf utilities.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <list>
#include <ostream>
#include <vector>

#include <process/clock.hpp>
#include <process/collect.hpp>
#include <process/defer.hpp>
#include <process/io.hpp>
#include <process/process.hpp>
#include <process/subprocess.hpp>

#include <stout/strings.hpp>

#include "linux/perf.hpp"

using std::list;
using std::ostringstream;
using std::set;
using std::string;
using std::vector;

using namespace process;

namespace perf {

// Delimiter for fields in perf stat output.
const string PERF_DELIMITER = ",";

// Use an empty string as the key for the parse output when sampling a
// set of pids. No valid cgroup can be an empty string.
const string PIDS_KEY = "";

namespace internal {

vector<string> argv(
    const set<string>& events,
    const set<string>& cgroups,
    const Duration& duration)
{
  vector<string> argv = {
    "perf", "stat",

    // System-wide collection from all CPUs.
    "--all-cpus",

    // Print counts using a CSV-style output to make it easy to import
    // directly into spreadsheets. Columns are separated by the string
    // specified in PERF_DELIMITER.
    "--field-separator", PERF_DELIMITER,

    // Ensure all output goes to stdout.
    "--log-fd", "1"
  };

  // Nested loop to produce all pairings of event and cgroup.
  foreach (const string& event, events) {
    foreach (const string& cgroup, cgroups) {
      argv.push_back("--event");
      argv.push_back(event);
      argv.push_back("--cgroup");
      argv.push_back(cgroup);
    }
  }

  argv.push_back("--");
  argv.push_back("sleep");
  argv.push_back(stringify(duration.secs()));

  return argv;
}

vector<string> argv(
    const set<string>& events,
    const string& cgroup,
    const Duration& duration)
{
  set<string> cgroups;
  cgroups.insert(cgroup);

  return argv(events, cgroups, duration);
}

vector<string> argv(
    const set<string>& events,
    const set<pid_t>& pids,
    const Duration& duration)
{
  vector<string> argv = {
    "perf", "stat",

    // System-wide collection from all CPUs.
    "--all-cpus",

    // Print counts using a CSV-style output to make it easy to import
    // directly into spreadsheets. Columns are separated by the string
    // specified in PERF_DELIMITER.
    "--field-separator", PERF_DELIMITER,

    // Ensure all output goes to stdout.
    "--log-fd", "1",

    "--event", strings::join(",", events),
    "--pid", strings::join(",", pids),
    "--",
    "sleep", stringify(duration.secs())
  };

  return argv;
}

// Normalize a perf event name. After normalization the event name
// should match an event field in the PerfStatistics protobuf.
inline string normalize(const string& s)
{
  string lower = strings::lower(s);
  return strings::replace(lower, "-", "_");
}

class PerfSampler : public Process<PerfSampler>
{
public:
  PerfSampler(const vector<string>& _argv, const Duration& _duration)
    : argv(_argv), duration(_duration) {}

  virtual ~PerfSampler() {}

  Future<hashmap<string, mesos::PerfStatistics>> future()
  {
    return promise.future();
  }

protected:
  virtual void initialize()
  {
    // Stop when no one cares.
    promise.future().onDiscard(lambda::bind(
          static_cast<void(*)(const UPID&, bool)>(terminate), self(), true));

    if (duration < Seconds(0)) {
      promise.fail("Perf sample duration cannot be negative: '" +
                    stringify(duration.secs()) + "'");
      terminate(self());
      return;
    }

    start = Clock::now();

    sample();
  }

  virtual void finalize()
  {
    discard(output);

    // Kill the perf process if it's still running.
    if (perf.isSome() && perf.get().status().isPending()) {
      kill(perf.get().pid(), SIGKILL);
    }

    promise.discard();
  }

private:
  void sample()
  {
    Try<Subprocess> _perf = subprocess(
        "perf",
        argv,
        Subprocess::PIPE(),
        Subprocess::PIPE(),
        Subprocess::PIPE());

    if (_perf.isError()) {
      promise.fail("Failed to launch perf process: " + _perf.error());
      terminate(self());
      return;
    }
    perf = _perf.get();

    // Start reading from stdout and stderr now. We don't use stderr
    // but must read from it to avoid the subprocess blocking on the
    // pipe.
    output.push_back(process::io::read(perf.get().out().get()));
    output.push_back(process::io::read(perf.get().err().get()));

    // Wait for the process to exit.
    perf.get().status()
      .onAny(defer(self(), &Self::_sample, lambda::_1));
  }

  void _sample(const Future<Option<int>>& status)
  {
    if (!status.isReady()) {
      promise.fail("Failed to get exit status of perf process: " +
                   (status.isFailed() ? status.failure() : "discarded"));
      terminate(self());
      return;
    }

    if (status.get().get() != 0) {
      promise.fail("Failed to execute perf, exit status: " +
                    stringify(WEXITSTATUS(status.get().get())));

      terminate(self());
      return;
    }

    // Wait until we collect all output.
    collect(output).onAny(defer(self(), &Self::__sample, lambda::_1));
  }

  void  __sample(const Future<list<string>>& future)
  {
    if (!future.isReady()) {
      promise.fail("Failed to collect output of perf process: " +
                   (future.isFailed() ? future.failure() : "discarded"));
      terminate(self());
      return;
    }

    // Parse output from stdout.
    Try<hashmap<string, mesos::PerfStatistics>> parse =
      perf::parse(output.front().get());
    if (parse.isError()) {
      promise.fail("Failed to parse perf output: " + parse.error());
      terminate(self());
      return;
    }

    // Create a non-const copy from the Try<> so we can set the
    // timestamp and duration.
    hashmap<string, mesos::PerfStatistics> statistics = parse.get();
    foreachvalue (mesos::PerfStatistics& s, statistics) {
      s.set_timestamp(start.secs());
      s.set_duration(duration.secs());
    }

    promise.set(statistics);
    terminate(self());
    return;
  }

  const vector<string> argv;
  const Duration duration;
  Time start;
  Option<Subprocess> perf;
  Promise<hashmap<string, mesos::PerfStatistics>> promise;
  list<Future<string>> output;
};

// Helper to select a single key from the hashmap of perf statistics.
Future<mesos::PerfStatistics> select(
    const string& key,
    const hashmap<string, mesos::PerfStatistics>& statistics)
{
  return statistics.get(key).get();
}

} // namespace internal {

Future<mesos::PerfStatistics> sample(
    const set<string>& events,
    pid_t pid,
    const Duration& duration)
{
  set<pid_t> pids;
  pids.insert(pid);
  return sample(events, pids, duration);
}

Future<mesos::PerfStatistics> sample(
    const set<string>& events,
    const set<pid_t>& pids,
    const Duration& duration)
{
  if (!supported()) {
    return Failure("Perf is not supported");
  }

  const vector<string> argv = internal::argv(events, pids, duration);
  internal::PerfSampler* sampler = new internal::PerfSampler(argv, duration);
  Future<hashmap<string, mesos::PerfStatistics>> future = sampler->future();
  spawn(sampler, true);
  return future
    .then(lambda::bind(&internal::select, PIDS_KEY, lambda::_1));
}

Future<mesos::PerfStatistics> sample(
    const set<string>& events,
    const string& cgroup,
    const Duration& duration)
{
  set<string> cgroups;
  cgroups.insert(cgroup);
  return sample(events, cgroups, duration)
    .then(lambda::bind(&internal::select, cgroup, lambda::_1));
}

Future<hashmap<string, mesos::PerfStatistics>> sample(
    const set<string>& events,
    const set<string>& cgroups,
    const Duration& duration)
{
  if (!supported()) {
    return Failure("Perf is not supported");
  }

  const vector<string> argv = internal::argv(events, cgroups, duration);
  internal::PerfSampler* sampler = new internal::PerfSampler(argv, duration);
  Future<hashmap<string, mesos::PerfStatistics>> future = sampler->future();
  spawn(sampler, true);
  return future;
}

bool valid(const set<string>& events)
{
  ostringstream command;

  // Log everything to stderr which is then redirected to /dev/null.
  command << "perf stat --log-fd 2";
  foreach (const string& event, events) {
    command << " --event " << event;
  }
  command << " true 2>/dev/null";

  return (os::system(command.str()) == 0);
}

bool supported()
{
  // Require Linux kernel version >= 2.6.38 for "-x" and >= 2.6.39 for
  // "--cgroup"
  Try<Version> release = os::release();

  // This is not expected to ever be an Error.
  CHECK_SOME(release);

  return release.get() >= Version(2, 6, 39);
}

Try<hashmap<string, mesos::PerfStatistics>> parse(const string& output)
{
  hashmap<string, mesos::PerfStatistics> statistics;

  foreach (const string& line, strings::tokenize(output, "\n")) {
    vector<string> tokens = strings::tokenize(line, PERF_DELIMITER);
    // Expected format for an output line is either:
    // value,event          (when sampling pids)
    // value,event,cgroup   (when sampling a cgroup)
    // assuming PERF_DELIMITER = ",".
    if (tokens.size() < 2 || tokens.size() > 3) {
      return Error("Unexpected perf output at line: " + line);
    }

    const string value = tokens[0];
    const string event = internal::normalize(tokens[1]);
    // Use the special PIDS_KEY when sampling pids.
    const string cgroup = (tokens.size() == 3 ? tokens[2] : PIDS_KEY);

    if (!statistics.contains(cgroup)) {
      statistics.put(cgroup, mesos::PerfStatistics());
    }

    const google::protobuf::Reflection* reflection =
      statistics[cgroup].GetReflection();
    const google::protobuf::FieldDescriptor* field =
      statistics[cgroup].GetDescriptor()->FindFieldByName(event);
    if (!field) {
      return Error("Unexpected perf output at line: " + line);
    }

    if (value == "<not supported>") {
      LOG(WARNING) << "Unsupported perf counter, ignoring: " << line;
      continue;
    }

    switch (field->type()) {
      case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
        {
          Try<double> number =
            (value == "<not counted>") ?  0 : numify<double>(value);

          if (number.isError()) {
            return Error("Unable to parse perf value at line: " + line);
          }

          reflection->SetDouble(&(statistics[cgroup]), field, number.get());
          break;
        }
      case google::protobuf::FieldDescriptor::TYPE_UINT64:
        {
          Try<uint64_t> number =
            (value == "<not counted>") ?  0 : numify<uint64_t>(value);

          if (number.isError()) {
            return Error("Unable to parse perf value at line: " + line);
          }

          reflection->SetUInt64(&(statistics[cgroup]), field, number.get());
          break;
        }
      default:
        return Error("Unsupported perf field type at line: " + line);
      }
  }

  return statistics;
}

} // namespace perf {
<|endoftext|>


<commit_before>// Copyright (c) 2020 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#include <algorithm> //For std::partition_copy and std::min_element.
#include <unordered_set>

#include "WallToolPaths.h"

#include "SkeletalTrapezoidation.h"
#include "BeadingStrategy/BeadingOrderOptimizer.h"
#include "utils/SparsePointGrid.h" //To stitch the inner contour.
#include "utils/polygonUtils.h"
#include "ExtruderTrain.h"

namespace cura
{
constexpr coord_t transition_length_multiplier = 2;

WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t nominal_bead_width, const size_t inset_count,
                             const Settings& settings)
    : outline(outline)
    , bead_width_0(nominal_bead_width)
    , bead_width_x(nominal_bead_width)
    , inset_count(inset_count)
    , strategy_type(settings.get<StrategyType>("beading_strategy_type"))
    , print_thin_walls(settings.get<bool>("fill_outline_gaps"))
    , min_feature_size(settings.get<coord_t>("min_feature_size"))
    , min_bead_width(settings.get<coord_t>("min_bead_width"))
    , small_area_length(INT2MM(static_cast<double>(nominal_bead_width) / 2))
    , transition_length(transition_length_multiplier * nominal_bead_width)
    , toolpaths_generated(false)
    , settings(settings)
{
}

WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x,
                             const size_t inset_count, const Settings& settings)
    : outline(outline)
    , bead_width_0(bead_width_0)
    , bead_width_x(bead_width_x)
    , inset_count(inset_count)
    , strategy_type(settings.get<StrategyType>("beading_strategy_type"))
    , print_thin_walls(settings.get<bool>("fill_outline_gaps"))
    , min_feature_size(settings.get<coord_t>("min_feature_size"))
    , min_bead_width(settings.get<coord_t>("min_bead_width"))
    , small_area_length(INT2MM(static_cast<double>(bead_width_0) / 2))
    , transition_length(transition_length_multiplier * bead_width_0)
    , toolpaths_generated(false)
    , settings(settings)
{
}

const VariableWidthPaths& WallToolPaths::generate()
{
    constexpr coord_t smallest_segment = 50;
    constexpr coord_t allowed_distance = 50;
    constexpr coord_t epsilon_offset = (allowed_distance / 2) - 1;
    constexpr float transitioning_angle = 0.5;
    constexpr coord_t discretization_step_size = 200;

    // Simplify outline for boost::voronoi consumption. Absolutely no self intersections or near-self intersections allowed:
    // TODO: Open question: Does this indeed fix all (or all-but-one-in-a-million) cases for manifold but otherwise possibly complex polygons?
    Polygons prepared_outline = outline.offset(-epsilon_offset).offset(epsilon_offset);
    prepared_outline.simplify(smallest_segment, allowed_distance);
    PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline);
    prepared_outline.removeDegenerateVerts();
    prepared_outline.removeColinearEdges();
    prepared_outline.removeSmallAreas(small_area_length * small_area_length, false);

    if (prepared_outline.area() > 0)
    {
        const size_t max_bead_count = 2 * inset_count;
        const auto beading_strat = std::unique_ptr<BeadingStrategy>(BeadingStrategyFactory::makeStrategy(
            strategy_type, bead_width_0, bead_width_x, transition_length, transitioning_angle, print_thin_walls, min_bead_width,
            min_feature_size, max_bead_count));
        const coord_t transition_filter_dist = beading_strat->optimal_width * 5;
        SkeletalTrapezoidation wall_maker(prepared_outline, *beading_strat, beading_strat->transitioning_angle, discretization_step_size, transition_filter_dist);
        wall_maker.generateToolpaths(toolpaths);
        computeInnerContour();
    }
    simplifyToolPaths(toolpaths, settings);

    removeEmptyToolPaths(toolpaths);
    toolpaths_generated = true;
    return toolpaths;
}

void WallToolPaths::simplifyToolPaths(VariableWidthPaths& toolpaths, const Settings& settings)
{
    for (size_t toolpaths_idx = 0; toolpaths_idx < toolpaths.size(); ++toolpaths_idx)
    {
        const ExtruderTrain& train_wall = settings.get<ExtruderTrain&>(toolpaths_idx == 0 ? "wall_0_extruder_nr" : "wall_x_extruder_nr");
        const coord_t maximum_resolution = train_wall.settings.get<coord_t>("meshfix_maximum_resolution");
        const coord_t maximum_deviation = train_wall.settings.get<coord_t>("meshfix_maximum_deviation");
        const coord_t maximum_extrusion_area_deviation = train_wall.settings.get<int>("meshfix_maximum_extrusion_area_deviation"); // unit: μm²
        for (auto& line : toolpaths[toolpaths_idx])
        {
            line.simplify(maximum_resolution, maximum_deviation, maximum_extrusion_area_deviation);
        }
    }
}

const VariableWidthPaths& WallToolPaths::getToolPaths()
{
    if (!toolpaths_generated)
    {
        return generate();
    }
    return toolpaths;
}

void WallToolPaths::computeInnerContour()
{
    //We'll remove all 0-width paths from the original toolpaths and store them separately as polygons.
    VariableWidthPaths actual_toolpaths;
    actual_toolpaths.reserve(toolpaths.size()); //A bit too much, but the correct order of magnitude.
    VariableWidthPaths contour_paths;
    contour_paths.reserve(toolpaths.size() / inset_count);
    std::partition_copy(toolpaths.begin(), toolpaths.end(), std::back_inserter(actual_toolpaths), std::back_inserter(contour_paths),
        [](const VariableWidthLines& path)
        {
            for(const ExtrusionLine& line : path)
            {
                for(const ExtrusionJunction& junction : line.junctions)
                {
                    return junction.w != 0; //On the first actual junction, decide: If it's got 0 width, this is a contour. Otherwise it is an actual toolpath.
                }
            }
            return true; //No junctions with any vertices? Classify it as a toolpath then.
        });
    toolpaths = std::move(actual_toolpaths); //Filtered out the 0-width paths.

    //Now convert the contour_paths to Polygons to denote the inner contour of the walled areas.
    inner_contour.clear();

    //We're going to have to stitch these paths since not all walls may be closed contours.
    //Since these walls have 0 width they should theoretically be closed. But there may be rounding errors.
    const coord_t minimum_line_width = bead_width_0 / 2;
    stitchContours(contour_paths, minimum_line_width, inner_contour);

    //The output walls from the skeletal trapezoidation have no known winding order, especially if they are joined together from polylines.
    //They can be in any direction, clockwise or counter-clockwise, regardless of whether the shapes are positive or negative.
    //To get a correct shape, we need to make the outside contour positive and any holes inside negative.
    //This can be done by applying the even-odd rule to the shape. This rule is not sensitive to the winding order of the polygon.
    //The even-odd rule would be incorrect if the polygon self-intersects, but that should never be generated by the skeletal trapezoidation.
    inner_contour = inner_contour.unionPolygons(Polygons(), ClipperLib::pftEvenOdd);
}

const Polygons& WallToolPaths::getInnerContour()
{
    if (!toolpaths_generated && inset_count > 0)
    {
        generate();
    }
    else if(inset_count == 0)
    {
        return outline;
    }
    return inner_contour;
}

bool WallToolPaths::removeEmptyToolPaths(VariableWidthPaths& toolpaths)
{
    toolpaths.erase(std::remove_if(toolpaths.begin(), toolpaths.end(), [](const VariableWidthLines& lines)
                                   {
                                       return lines.empty();
                                   }), toolpaths.end());
    return toolpaths.empty();
}

void WallToolPaths::stitchContours(const VariableWidthPaths& input, const coord_t stitch_distance, Polygons& output) const
{
    //Create a bucket grid to find endpoints that are close together.
    struct ExtrusionLineStartLocator
    {
        Point operator()(const ExtrusionLine* line)
        {
            return Point(line->junctions.front().p);
        }
    };
    struct ExtrusionLineEndLocator
    {
        Point operator()(const ExtrusionLine* line)
        {
            return Point(line->junctions.back().p);
        }
    };
    SparsePointGrid<const ExtrusionLine*, ExtrusionLineStartLocator> line_starts(stitch_distance); //Only find endpoints closer than minimum_line_width, so we can't ever accidentally make crossing contours.
    SparsePointGrid<const ExtrusionLine*, ExtrusionLineEndLocator> line_ends(stitch_distance);
    for(const VariableWidthLines& path : input)
    {
        for(const ExtrusionLine& line : path)
        {
            line_starts.insert(&line);
            line_ends.insert(&line);
        }
    }
    //Then go through all lines and construct chains of polylines if the endpoints are nearby.
    std::unordered_set<const ExtrusionLine*> processed_lines; //Track which lines were already processed to not process them twice.
    for(const VariableWidthLines& path : input)
    {
        for(const ExtrusionLine& line : path)
        {
            if(processed_lines.find(&line) != processed_lines.end()) //We already added this line before. It got added as a nearby line.
            {
                continue;
            }
            //We'll create a chain of polylines that get joined together. We can add polylines on both ends!
            std::deque<const ExtrusionLine*> chain;
            std::deque<bool> is_reversed; //Lines could need to be inserted in reverse. Must coincide with the `chain` deque.
            const ExtrusionLine* nearest = &line; //At every iteration, add the polyline that joins together most closely.
            bool nearest_reverse = false; //Whether the next line to insert must be inserted in reverse.
            bool nearest_before = false; //Whether the next line to insert must be inserted in the front of the chain.
            while(nearest)
            {
                if(processed_lines.find(nearest) != processed_lines.end())
                {
                    break; //Looping. This contour is already processed.
                }
                processed_lines.insert(nearest);
                if(nearest_before)
                {
                    chain.push_front(nearest);
                    is_reversed.push_front(nearest_reverse);
                }
                else
                {
                    chain.push_back(nearest);
                    is_reversed.push_back(nearest_reverse);
                }

                //Find any nearby lines to attach. Look on both ends of our current chain and find both ends of polylines.
                const Point chain_start = is_reversed.front() ? chain.front()->junctions.back().p : chain.front()->junctions.front().p;
                const Point chain_end = is_reversed.back() ? chain.back()->junctions.front().p : chain.back()->junctions.back().p;
                std::vector<const ExtrusionLine*> starts_near_start = line_starts.getNearby(chain_start, stitch_distance);
                std::vector<const ExtrusionLine*> ends_near_start = line_ends.getNearby(chain_start, stitch_distance);
                std::vector<const ExtrusionLine*> starts_near_end = line_starts.getNearby(chain_end, stitch_distance);
                std::vector<const ExtrusionLine*> ends_near_end = line_ends.getNearby(chain_end, stitch_distance);

                nearest = nullptr;
                coord_t nearest_dist2 = std::numeric_limits<coord_t>::max();
                for(const ExtrusionLine* candidate : starts_near_start)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue; //Already processed this line before. It's linked to something else.
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.front().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = true;
                        nearest_before = true;
                    }
                }
                for(const ExtrusionLine* candidate : ends_near_start)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue;
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.back().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = false;
                        nearest_before = true;
                    }
                }
                for(const ExtrusionLine* candidate : starts_near_end)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue; //Already processed this line before. It's linked to something else.
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.front().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = false;
                        nearest_before = false;
                    }
                }
                for(const ExtrusionLine* candidate : ends_near_end)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue;
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.back().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = true;
                        nearest_before = false;
                    }
                }
            }

            //Now serialize the entire chain into one polygon.
            output.emplace_back();
            for(size_t i = 0; i < chain.size(); ++i)
            {
                if(!is_reversed[i])
                {
                    for(const ExtrusionJunction& junction : chain[i]->junctions)
                    {
                        output.back().add(junction.p);
                    }
                }
                else
                {
                    for(auto junction = chain[i]->junctions.rbegin(); junction != chain[i]->junctions.rend(); ++junction)
                    {
                        output.back().add(junction->p);
                    }
                }
            }
        }
    }
}

} // namespace cura
<commit_msg>Remove some hardcoded values<commit_after>// Copyright (c) 2020 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#include <algorithm> //For std::partition_copy and std::min_element.
#include <unordered_set>

#include "WallToolPaths.h"

#include "SkeletalTrapezoidation.h"
#include "BeadingStrategy/BeadingOrderOptimizer.h"
#include "utils/SparsePointGrid.h" //To stitch the inner contour.
#include "utils/polygonUtils.h"
#include "ExtruderTrain.h"

namespace cura
{
constexpr coord_t transition_length_multiplier = 2;

WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t nominal_bead_width, const size_t inset_count,
                             const Settings& settings)
    : outline(outline)
    , bead_width_0(nominal_bead_width)
    , bead_width_x(nominal_bead_width)
    , inset_count(inset_count)
    , strategy_type(settings.get<StrategyType>("beading_strategy_type"))
    , print_thin_walls(settings.get<bool>("fill_outline_gaps"))
    , min_feature_size(settings.get<coord_t>("min_feature_size"))
    , min_bead_width(settings.get<coord_t>("min_bead_width"))
    , small_area_length(INT2MM(static_cast<double>(nominal_bead_width) / 2))
    , transition_length(transition_length_multiplier * nominal_bead_width)
    , toolpaths_generated(false)
    , settings(settings)
{
}

WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x,
                             const size_t inset_count, const Settings& settings)
    : outline(outline)
    , bead_width_0(bead_width_0)
    , bead_width_x(bead_width_x)
    , inset_count(inset_count)
    , strategy_type(settings.get<StrategyType>("beading_strategy_type"))
    , print_thin_walls(settings.get<bool>("fill_outline_gaps"))
    , min_feature_size(settings.get<coord_t>("min_feature_size"))
    , min_bead_width(settings.get<coord_t>("min_bead_width"))
    , small_area_length(INT2MM(static_cast<double>(bead_width_0) / 2))
    , transition_length(transition_length_multiplier * bead_width_0)
    , toolpaths_generated(false)
    , settings(settings)
{
}

const VariableWidthPaths& WallToolPaths::generate()
{
    const coord_t smallest_segment = settings.get<coord_t>("meshfix_maximum_resolution");
    const coord_t allowed_distance = settings.get<coord_t>("meshfix_maximum_deviation");
    const coord_t epsilon_offset = (allowed_distance / 2) - 1;
    constexpr float transitioning_angle = 0.5;
    constexpr coord_t discretization_step_size = 200;

    // Simplify outline for boost::voronoi consumption. Absolutely no self intersections or near-self intersections allowed:
    // TODO: Open question: Does this indeed fix all (or all-but-one-in-a-million) cases for manifold but otherwise possibly complex polygons?
    Polygons prepared_outline = outline.offset(-epsilon_offset).offset(epsilon_offset);
    prepared_outline.simplify(smallest_segment, allowed_distance);
    PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline);
    prepared_outline.removeDegenerateVerts();
    prepared_outline.removeColinearEdges();
    prepared_outline.removeSmallAreas(small_area_length * small_area_length, false);

    if (prepared_outline.area() > 0)
    {
        const size_t max_bead_count = 2 * inset_count;
        const auto beading_strat = std::unique_ptr<BeadingStrategy>(BeadingStrategyFactory::makeStrategy(
            strategy_type, bead_width_0, bead_width_x, transition_length, transitioning_angle, print_thin_walls, min_bead_width,
            min_feature_size, max_bead_count));
        const coord_t transition_filter_dist = beading_strat->optimal_width * 5;
        SkeletalTrapezoidation wall_maker(prepared_outline, *beading_strat, beading_strat->transitioning_angle, discretization_step_size, transition_filter_dist);
        wall_maker.generateToolpaths(toolpaths);
        computeInnerContour();
    }
    simplifyToolPaths(toolpaths, settings);

    removeEmptyToolPaths(toolpaths);
    toolpaths_generated = true;
    return toolpaths;
}

void WallToolPaths::simplifyToolPaths(VariableWidthPaths& toolpaths, const Settings& settings)
{
    for (size_t toolpaths_idx = 0; toolpaths_idx < toolpaths.size(); ++toolpaths_idx)
    {
        const ExtruderTrain& train_wall = settings.get<ExtruderTrain&>(toolpaths_idx == 0 ? "wall_0_extruder_nr" : "wall_x_extruder_nr");
        const coord_t maximum_resolution = train_wall.settings.get<coord_t>("meshfix_maximum_resolution");
        const coord_t maximum_deviation = train_wall.settings.get<coord_t>("meshfix_maximum_deviation");
        const coord_t maximum_extrusion_area_deviation = train_wall.settings.get<int>("meshfix_maximum_extrusion_area_deviation"); // unit: μm²
        for (auto& line : toolpaths[toolpaths_idx])
        {
            line.simplify(maximum_resolution, maximum_deviation, maximum_extrusion_area_deviation);
        }
    }
}

const VariableWidthPaths& WallToolPaths::getToolPaths()
{
    if (!toolpaths_generated)
    {
        return generate();
    }
    return toolpaths;
}

void WallToolPaths::computeInnerContour()
{
    //We'll remove all 0-width paths from the original toolpaths and store them separately as polygons.
    VariableWidthPaths actual_toolpaths;
    actual_toolpaths.reserve(toolpaths.size()); //A bit too much, but the correct order of magnitude.
    VariableWidthPaths contour_paths;
    contour_paths.reserve(toolpaths.size() / inset_count);
    std::partition_copy(toolpaths.begin(), toolpaths.end(), std::back_inserter(actual_toolpaths), std::back_inserter(contour_paths),
        [](const VariableWidthLines& path)
        {
            for(const ExtrusionLine& line : path)
            {
                for(const ExtrusionJunction& junction : line.junctions)
                {
                    return junction.w != 0; //On the first actual junction, decide: If it's got 0 width, this is a contour. Otherwise it is an actual toolpath.
                }
            }
            return true; //No junctions with any vertices? Classify it as a toolpath then.
        });
    toolpaths = std::move(actual_toolpaths); //Filtered out the 0-width paths.

    //Now convert the contour_paths to Polygons to denote the inner contour of the walled areas.
    inner_contour.clear();

    //We're going to have to stitch these paths since not all walls may be closed contours.
    //Since these walls have 0 width they should theoretically be closed. But there may be rounding errors.
    const coord_t minimum_line_width = bead_width_0 / 2;
    stitchContours(contour_paths, minimum_line_width, inner_contour);

    //The output walls from the skeletal trapezoidation have no known winding order, especially if they are joined together from polylines.
    //They can be in any direction, clockwise or counter-clockwise, regardless of whether the shapes are positive or negative.
    //To get a correct shape, we need to make the outside contour positive and any holes inside negative.
    //This can be done by applying the even-odd rule to the shape. This rule is not sensitive to the winding order of the polygon.
    //The even-odd rule would be incorrect if the polygon self-intersects, but that should never be generated by the skeletal trapezoidation.
    inner_contour = inner_contour.unionPolygons(Polygons(), ClipperLib::pftEvenOdd);
}

const Polygons& WallToolPaths::getInnerContour()
{
    if (!toolpaths_generated && inset_count > 0)
    {
        generate();
    }
    else if(inset_count == 0)
    {
        return outline;
    }
    return inner_contour;
}

bool WallToolPaths::removeEmptyToolPaths(VariableWidthPaths& toolpaths)
{
    toolpaths.erase(std::remove_if(toolpaths.begin(), toolpaths.end(), [](const VariableWidthLines& lines)
                                   {
                                       return lines.empty();
                                   }), toolpaths.end());
    return toolpaths.empty();
}

void WallToolPaths::stitchContours(const VariableWidthPaths& input, const coord_t stitch_distance, Polygons& output) const
{
    //Create a bucket grid to find endpoints that are close together.
    struct ExtrusionLineStartLocator
    {
        Point operator()(const ExtrusionLine* line)
        {
            return Point(line->junctions.front().p);
        }
    };
    struct ExtrusionLineEndLocator
    {
        Point operator()(const ExtrusionLine* line)
        {
            return Point(line->junctions.back().p);
        }
    };
    SparsePointGrid<const ExtrusionLine*, ExtrusionLineStartLocator> line_starts(stitch_distance); //Only find endpoints closer than minimum_line_width, so we can't ever accidentally make crossing contours.
    SparsePointGrid<const ExtrusionLine*, ExtrusionLineEndLocator> line_ends(stitch_distance);
    for(const VariableWidthLines& path : input)
    {
        for(const ExtrusionLine& line : path)
        {
            line_starts.insert(&line);
            line_ends.insert(&line);
        }
    }
    //Then go through all lines and construct chains of polylines if the endpoints are nearby.
    std::unordered_set<const ExtrusionLine*> processed_lines; //Track which lines were already processed to not process them twice.
    for(const VariableWidthLines& path : input)
    {
        for(const ExtrusionLine& line : path)
        {
            if(processed_lines.find(&line) != processed_lines.end()) //We already added this line before. It got added as a nearby line.
            {
                continue;
            }
            //We'll create a chain of polylines that get joined together. We can add polylines on both ends!
            std::deque<const ExtrusionLine*> chain;
            std::deque<bool> is_reversed; //Lines could need to be inserted in reverse. Must coincide with the `chain` deque.
            const ExtrusionLine* nearest = &line; //At every iteration, add the polyline that joins together most closely.
            bool nearest_reverse = false; //Whether the next line to insert must be inserted in reverse.
            bool nearest_before = false; //Whether the next line to insert must be inserted in the front of the chain.
            while(nearest)
            {
                if(processed_lines.find(nearest) != processed_lines.end())
                {
                    break; //Looping. This contour is already processed.
                }
                processed_lines.insert(nearest);
                if(nearest_before)
                {
                    chain.push_front(nearest);
                    is_reversed.push_front(nearest_reverse);
                }
                else
                {
                    chain.push_back(nearest);
                    is_reversed.push_back(nearest_reverse);
                }

                //Find any nearby lines to attach. Look on both ends of our current chain and find both ends of polylines.
                const Point chain_start = is_reversed.front() ? chain.front()->junctions.back().p : chain.front()->junctions.front().p;
                const Point chain_end = is_reversed.back() ? chain.back()->junctions.front().p : chain.back()->junctions.back().p;
                std::vector<const ExtrusionLine*> starts_near_start = line_starts.getNearby(chain_start, stitch_distance);
                std::vector<const ExtrusionLine*> ends_near_start = line_ends.getNearby(chain_start, stitch_distance);
                std::vector<const ExtrusionLine*> starts_near_end = line_starts.getNearby(chain_end, stitch_distance);
                std::vector<const ExtrusionLine*> ends_near_end = line_ends.getNearby(chain_end, stitch_distance);

                nearest = nullptr;
                coord_t nearest_dist2 = std::numeric_limits<coord_t>::max();
                for(const ExtrusionLine* candidate : starts_near_start)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue; //Already processed this line before. It's linked to something else.
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.front().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = true;
                        nearest_before = true;
                    }
                }
                for(const ExtrusionLine* candidate : ends_near_start)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue;
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.back().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = false;
                        nearest_before = true;
                    }
                }
                for(const ExtrusionLine* candidate : starts_near_end)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue; //Already processed this line before. It's linked to something else.
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.front().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = false;
                        nearest_before = false;
                    }
                }
                for(const ExtrusionLine* candidate : ends_near_end)
                {
                    if(processed_lines.find(candidate) != processed_lines.end())
                    {
                        continue;
                    }
                    const coord_t dist2 = vSize2(candidate->junctions.back().p - chain_start);
                    if(dist2 < nearest_dist2)
                    {
                        nearest = candidate;
                        nearest_dist2 = dist2;
                        nearest_reverse = true;
                        nearest_before = false;
                    }
                }
            }

            //Now serialize the entire chain into one polygon.
            output.emplace_back();
            for(size_t i = 0; i < chain.size(); ++i)
            {
                if(!is_reversed[i])
                {
                    for(const ExtrusionJunction& junction : chain[i]->junctions)
                    {
                        output.back().add(junction.p);
                    }
                }
                else
                {
                    for(auto junction = chain[i]->junctions.rbegin(); junction != chain[i]->junctions.rend(); ++junction)
                    {
                        output.back().add(junction->p);
                    }
                }
            }
        }
    }
}

} // namespace cura
<|endoftext|>


<commit_before>//===-- ManifestLoader.cpp ------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "llbuild/Ninja/ManifestLoader.h"

#include "llbuild/Ninja/Lexer.h"
#include "llbuild/Ninja/Parser.h"

#include <cstdlib>
#include <sstream>
#include <vector>

using namespace llbuild;
using namespace llbuild::ninja;

#pragma mark - ManifestLoaderActions

ManifestLoaderActions::~ManifestLoaderActions() {
}

#pragma mark - ManifestLoader Implementation

namespace {

/// Manifest loader implementation.
///
/// For simplicity, we just directly implement the parser actions interface.
class ManifestLoaderImpl: public ParseActions {
  struct IncludeEntry {
    /// The file that is being processed.
    std::string Filename;
    /// An owning reference to the data consumed by the parser.
    std::unique_ptr<char[]> Data;
    /// The parser for the file.
    std::unique_ptr<Parser> Parser;

    IncludeEntry(const std::string& Filename,
                 std::unique_ptr<char[]> Data,
                 std::unique_ptr<class Parser> Parser)
      : Filename(Filename), Data(std::move(Data)), Parser(std::move(Parser)) {}
  };

  std::string MainFilename;
  ManifestLoaderActions &Actions;
  std::unique_ptr<Manifest> TheManifest;
  std::vector<IncludeEntry> IncludeStack;

public:
  ManifestLoaderImpl(std::string MainFilename, ManifestLoaderActions &Actions)
    : MainFilename(MainFilename), Actions(Actions), TheManifest(nullptr)
  {
  }

  std::unique_ptr<Manifest> load() {
    // Create the manifest.
    TheManifest.reset(new Manifest);

    // Enter the main file.
    if (!enterFile(MainFilename))
      return nullptr;

    // Run the parser.
    assert(IncludeStack.size() == 1);
    getCurrentParser()->parse();
    assert(IncludeStack.size() == 0);

    return std::move(TheManifest);
  }

  bool enterFile(const std::string& Filename,
                 const Token* ForToken = nullptr) {
    // Load the file data.
    std::unique_ptr<char[]> Data;
    uint64_t Length;
    std::string FromFilename = IncludeStack.empty() ? Filename :
      getCurrentFilename();
    if (!Actions.readFileContents(FromFilename, Filename, ForToken, &Data,
                                  &Length))
      return false;

    // Push a new entry onto the include stack.
    std::unique_ptr<Parser> FileParser(new Parser(Data.get(), Length, *this));
    IncludeStack.push_back(IncludeEntry(Filename, std::move(Data),
                                        std::move(FileParser)));

    return true;
  }

  void exitCurrentFile() {
    IncludeStack.pop_back();
  }

  ManifestLoaderActions& getActions() { return Actions; }
  Parser* getCurrentParser() const {
    assert(!IncludeStack.empty());
    return IncludeStack.back().Parser.get();
  }
  const std::string& getCurrentFilename() const {
    assert(!IncludeStack.empty());
    return IncludeStack.back().Filename;
  }

  /// Given a string template token, evaluate it against the given \arg Bindings
  /// and return the resulting string.
  std::string evalString(const Token& Value, const BindingSet& Bindings) {
    assert(Value.TokenKind == Token::Kind::String && "invalid token kind");

    // Scan the string for escape sequences or variable references, accumulating
    // output pieces as we go.
    //
    // FIXME: Rewrite this with StringRef once we have it, and make efficient.
    std::stringstream Result;
    const char* Start = Value.Start;
    const char* End = Value.Start + Value.Length;
    const char* Pos = Start;
    while (Pos != End) {
      // Find the next '$'.
      const char* PieceStart = Pos;
      for (; Pos != End; ++Pos) {
        if (*Pos == '$')
          break;
      }

      // Add the current piece, if non-empty.
      if (Pos != PieceStart)
        Result << std::string(PieceStart, Pos);

      // If we are at the end, we are done.
      if (Pos == End)
        break;

      // Otherwise, we have a '$' character to handle.
      ++Pos;
      if (Pos == End) {
        error("invalid '$'-escape at end of string", Value);
        break;
      }

      // If this is a newline continuation, skip it and all leading space.
      char Char = *Pos;
      if (Char == '\n') {
        ++Pos;
        while (Pos != End && isspace(*Pos))
          ++Pos;
        continue;
      }

      // If this is single character escape, honor it.
      if (Char == ' ' || Char == ':' || Char == '$') {
        Result << Char;
        ++Pos;
        continue;
      }

      // If this is a braced variable reference, expand it.
      if (Char == '{') {
        // Scan until the end of the reference, checking validity of the
        // identifier name as we go.
        ++Pos;
        const char* VarStart = Pos;
        bool IsValid = true;
        while (true) {
          // If we reached the end of the string, this is an error.
          if (Pos == End) {
            error("invalid variable reference in string (missing trailing '}')",
                  Value);
            break;
          }

          // If we found the end of the reference, resolve it.
          char Char = *Pos;
          if (Char == '}') {
            // If this identifier isn't valid, emit an error.
            if (!IsValid) {
              error("invalid variable name in reference", Value);
            } else {
              Result << Bindings.lookup(std::string(VarStart, Pos - VarStart));
            }
            ++Pos;
            break;
          }

          // Track whether this is a valid identifier.
          if (!Lexer::isIdentifierChar(Char))
            IsValid = false;

          ++Pos;
        }
        continue;
      }

      // If this is a simple variable reference, expand it.
      if (Lexer::isSimpleIdentifierChar(Char)) {
        const char* VarStart = Pos;
        // Scan until the end of the simple identifier.
        ++Pos;
        while (Pos != End && Lexer::isSimpleIdentifierChar(*Pos))
          ++Pos;
        Result << Bindings.lookup(std::string(VarStart, Pos-VarStart));
        continue;
      }

      // Otherwise, we have an invalid '$' escape.
      error("invalid '$'-escape (literal '$' should be written as '$$')",
            Value);
      break;
    }

    return Result.str();
  }

  /// @name Parse Actions Interfaces
  /// @{

  virtual void initialize(ninja::Parser *Parser) override { }

  virtual void error(std::string Message, const Token &At) override {
    Actions.error(getCurrentFilename(), Message, At);
  }

  virtual void actOnBeginManifest(std::string Name) override { }

  virtual void actOnEndManifest() override {
    exitCurrentFile();
  }

  virtual void actOnBindingDecl(const Token& NameTok,
                                const Token& ValueTok) override {
    // Extract the name string.
    std::string Name(NameTok.Start, NameTok.Length);

    // Evaluate the value string with the current top-level bindings.
    std::string Value(evalString(ValueTok, TheManifest->getBindings()));

    TheManifest->getBindings().insert(Name, Value);
  }

  virtual void actOnDefaultDecl(const std::vector<Token>& NameToks) override {
    // Resolve all of the inputs and outputs.
    for (auto& NameTok: NameToks) {
      std::string Name(NameTok.Start, NameTok.Length);

      auto it = TheManifest->getNodes().find(Name);
      if (it == TheManifest->getNodes().end()) {
        error("unknown target name", NameTok);
        continue;
      }

      TheManifest->getDefaultTargets().push_back(it->second.get());
    }
  }

  virtual void actOnIncludeDecl(bool IsInclude,
                                const Token& PathTok) override {
    std::string Path = evalString(PathTok, TheManifest->getBindings());

    // Enter the new file.
    //
    // FIXME: Need to handle proper changes to the binding scope.
    if (enterFile(Path, &PathTok)) {
      // Run the parser for the included file.
      getCurrentParser()->parse();
    }
  }

  virtual BuildResult
  actOnBeginBuildDecl(const Token& NameTok,
                      const std::vector<Token> &OutputTokens,
                      const std::vector<Token> &InputTokens,
                      unsigned NumExplicitInputs,
                      unsigned NumImplicitInputs) override {
    std::string Name(NameTok.Start, NameTok.Length);

    // Resolve the rule.
    auto it = TheManifest->getRules().find(Name);
    Rule *Rule;
    if (it == TheManifest->getRules().end()) {
      error("unknown rule", NameTok);

      // Ensure we always have a rule for each command.
      Rule = TheManifest->getPhonyRule();
    } else {
      Rule = it->second.get();
    }

    // Resolve all of the inputs and outputs.
    std::vector<Node*> Outputs;
    std::vector<Node*> Inputs;
    for (auto& Token: OutputTokens) {
      // Evaluate the token string.
      std::string Path = evalString(Token, TheManifest->getBindings());
      Outputs.push_back(TheManifest->getOrCreateNode(Path));
    }
    for (auto& Token: InputTokens) {
      // Evaluate the token string.
      std::string Path = evalString(Token, TheManifest->getBindings());
      Inputs.push_back(TheManifest->getOrCreateNode(Path));
    }

    Command *Decl = new Command(Rule, Outputs, Inputs, NumExplicitInputs,
                                   NumImplicitInputs);
    TheManifest->getCommands().push_back(std::unique_ptr<Command>(Decl));

    return Decl;
  }

  virtual void actOnBuildBindingDecl(BuildResult AbstractDecl,
                                     const Token& NameTok,
                                     const Token& ValueTok) override {
    Command* Decl = static_cast<Command*>(AbstractDecl);

    std::string Name(NameTok.Start, NameTok.Length);

    // FIXME: It probably should be an error to assign to the same parameter
    // multiple times, but Ninja doesn't diagnose this.

    // The value in a build decl is always evaluated immediately, but only in
    // the context of the top-level bindings.
    //
    // FIXME: This needs to be the inherited bindings, once we support includes.
    Decl->getParameters()[Name] = evalString(ValueTok,
                                             TheManifest->getBindings());
  }

  virtual void actOnEndBuildDecl(PoolResult Decl,
                                const Token& StartTok) override { }

  virtual PoolResult actOnBeginPoolDecl(const Token& NameTok) override {
    std::string Name(NameTok.Start, NameTok.Length);

    // Find the hash slot.
    auto& Result = TheManifest->getPools()[Name];

    // Diagnose if the pool already exists (we still create a new one).
    if (Result.get()) {
      // The ppol already exists.
      error("duplicate pool", NameTok);
    }

    // Insert the new pool.
    Pool* Decl = new Pool(Name);
    Result.reset(Decl);
    return static_cast<PoolResult>(Decl);
  }

  virtual void actOnPoolBindingDecl(PoolResult AbstractDecl,
                                    const Token& NameTok,
                                    const Token& ValueTok) override {
    Pool* Decl = static_cast<Pool*>(AbstractDecl);

    std::string Name(NameTok.Start, NameTok.Length);

    // Evaluate the value string with the current top-level bindings.
    std::string Value(evalString(ValueTok, TheManifest->getBindings()));

    if (Name == "depth") {
      const char* Start = Value.c_str();
      char* End;
      long IntValue = ::strtol(Start, &End, 10);
      if (*End != '\0' || IntValue <= 0) {
        error("invalid depth", ValueTok);
      } else {
        Decl->setDepth(static_cast<uint32_t>(IntValue));
      }
    } else {
      error("unexpected variable", NameTok);
    }
  }

  virtual void actOnEndPoolDecl(PoolResult AbstractDecl,
                                const Token& StartTok) override {
    Pool* Decl = static_cast<Pool*>(AbstractDecl);

    // It is an error to not specify the pool depth.
    if (Decl->getDepth() == 0) {
      error("missing 'depth' variable assignment", StartTok);
    }
  }

  virtual RuleResult actOnBeginRuleDecl(const Token& NameTok) override {
    std::string Name(NameTok.Start, NameTok.Length);

    // Find the hash slot.
    auto& Result = TheManifest->getRules()[Name];

    // Diagnose if the rule already exists (we still create a new one).
    if (Result.get()) {
      // The rule already exists.
      error("duplicate rule", NameTok);
    }

    // Insert the new rule.
    Rule* Decl = new Rule(Name);
    Result.reset(Decl);
    return static_cast<RuleResult>(Decl);
  }

  virtual void actOnRuleBindingDecl(RuleResult AbstractDecl,
                                    const Token& NameTok,
                                    const Token& ValueTok) override {
    Rule* Decl = static_cast<Rule*>(AbstractDecl);

    std::string Name(NameTok.Start, NameTok.Length);
    // FIXME: It probably should be an error to assign to the same parameter
    // multiple times, but Ninja doesn't diagnose this.
    if (Rule::isValidParameterName(Name)) {
      Decl->getParameters()[Name] = std::string(ValueTok.Start,
                                                ValueTok.Length);
    } else {
      error("unexpected variable", NameTok);
    }
  }

  virtual void actOnEndRuleDecl(RuleResult AbstractDecl,
                                const Token& StartTok) override {
    Rule* Decl = static_cast<Rule*>(AbstractDecl);

    if (!Decl->getParameters().count("command")) {
      error("missing 'command' variable assignment", StartTok);
    }
  }

  /// @}
};

}

#pragma mark - ManifestLoader

ManifestLoader::ManifestLoader(std::string Filename,
                               ManifestLoaderActions &Actions)
  : Impl(static_cast<void*>(new ManifestLoaderImpl(Filename, Actions)))
{
}

ManifestLoader::~ManifestLoader() {
  delete static_cast<ManifestLoaderImpl*>(Impl);
}

std::unique_ptr<Manifest> ManifestLoader::load() {
  // Initialize the actions.
  static_cast<ManifestLoaderImpl*>(Impl)->getActions().initialize(this);

  return static_cast<ManifestLoaderImpl*>(Impl)->load();
}

const Parser* ManifestLoader::getCurrentParser() const {
  return static_cast<const ManifestLoaderImpl*>(Impl)->getCurrentParser();
}

<commit_msg>[Ninja/ManifestLoader] Update include stack to also track active binding set.<commit_after>//===-- ManifestLoader.cpp ------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "llbuild/Ninja/ManifestLoader.h"

#include "llbuild/Ninja/Lexer.h"
#include "llbuild/Ninja/Parser.h"

#include <cstdlib>
#include <sstream>
#include <vector>

using namespace llbuild;
using namespace llbuild::ninja;

#pragma mark - ManifestLoaderActions

ManifestLoaderActions::~ManifestLoaderActions() {
}

#pragma mark - ManifestLoader Implementation

namespace {

/// Manifest loader implementation.
///
/// For simplicity, we just directly implement the parser actions interface.
class ManifestLoaderImpl: public ParseActions {
  struct IncludeEntry {
    /// The file that is being processed.
    std::string Filename;
    /// An owning reference to the data consumed by the parser.
    std::unique_ptr<char[]> Data;
    /// The parser for the file.
    std::unique_ptr<Parser> Parser;
    /// The active binding set.
    BindingSet &Bindings;

    IncludeEntry(const std::string& Filename,
                 std::unique_ptr<char[]> Data,
                 std::unique_ptr<class Parser> Parser,
                 BindingSet &Bindings)
      : Filename(Filename), Data(std::move(Data)), Parser(std::move(Parser)),
        Bindings(Bindings) {}
  };

  std::string MainFilename;
  ManifestLoaderActions &Actions;
  std::unique_ptr<Manifest> TheManifest;
  std::vector<IncludeEntry> IncludeStack;

public:
  ManifestLoaderImpl(std::string MainFilename, ManifestLoaderActions &Actions)
    : MainFilename(MainFilename), Actions(Actions), TheManifest(nullptr)
  {
  }

  std::unique_ptr<Manifest> load() {
    // Create the manifest.
    TheManifest.reset(new Manifest);

    // Enter the main file.
    if (!enterFile(MainFilename, TheManifest->getBindings()))
      return nullptr;

    // Run the parser.
    assert(IncludeStack.size() == 1);
    getCurrentParser()->parse();
    assert(IncludeStack.size() == 0);

    return std::move(TheManifest);
  }

  bool enterFile(const std::string& Filename, BindingSet& Bindings,
                 const Token* ForToken = nullptr) {
    // Load the file data.
    std::unique_ptr<char[]> Data;
    uint64_t Length;
    std::string FromFilename = IncludeStack.empty() ? Filename :
      getCurrentFilename();
    if (!Actions.readFileContents(FromFilename, Filename, ForToken, &Data,
                                  &Length))
      return false;

    // Push a new entry onto the include stack.
    std::unique_ptr<Parser> FileParser(new Parser(Data.get(), Length, *this));
    IncludeStack.push_back(IncludeEntry(Filename, std::move(Data),
                                        std::move(FileParser),
                                        Bindings));

    return true;
  }

  void exitCurrentFile() {
    IncludeStack.pop_back();
  }

  ManifestLoaderActions& getActions() { return Actions; }
  Parser* getCurrentParser() const {
    assert(!IncludeStack.empty());
    return IncludeStack.back().Parser.get();
  }
  const std::string& getCurrentFilename() const {
    assert(!IncludeStack.empty());
    return IncludeStack.back().Filename;
  }
  BindingSet& getCurrentBindings() const {
    assert(!IncludeStack.empty());
    return IncludeStack.back().Bindings;
  }

  /// Given a string template token, evaluate it against the given \arg Bindings
  /// and return the resulting string.
  std::string evalString(const Token& Value, const BindingSet& Bindings) {
    assert(Value.TokenKind == Token::Kind::String && "invalid token kind");

    // Scan the string for escape sequences or variable references, accumulating
    // output pieces as we go.
    //
    // FIXME: Rewrite this with StringRef once we have it, and make efficient.
    std::stringstream Result;
    const char* Start = Value.Start;
    const char* End = Value.Start + Value.Length;
    const char* Pos = Start;
    while (Pos != End) {
      // Find the next '$'.
      const char* PieceStart = Pos;
      for (; Pos != End; ++Pos) {
        if (*Pos == '$')
          break;
      }

      // Add the current piece, if non-empty.
      if (Pos != PieceStart)
        Result << std::string(PieceStart, Pos);

      // If we are at the end, we are done.
      if (Pos == End)
        break;

      // Otherwise, we have a '$' character to handle.
      ++Pos;
      if (Pos == End) {
        error("invalid '$'-escape at end of string", Value);
        break;
      }

      // If this is a newline continuation, skip it and all leading space.
      char Char = *Pos;
      if (Char == '\n') {
        ++Pos;
        while (Pos != End && isspace(*Pos))
          ++Pos;
        continue;
      }

      // If this is single character escape, honor it.
      if (Char == ' ' || Char == ':' || Char == '$') {
        Result << Char;
        ++Pos;
        continue;
      }

      // If this is a braced variable reference, expand it.
      if (Char == '{') {
        // Scan until the end of the reference, checking validity of the
        // identifier name as we go.
        ++Pos;
        const char* VarStart = Pos;
        bool IsValid = true;
        while (true) {
          // If we reached the end of the string, this is an error.
          if (Pos == End) {
            error("invalid variable reference in string (missing trailing '}')",
                  Value);
            break;
          }

          // If we found the end of the reference, resolve it.
          char Char = *Pos;
          if (Char == '}') {
            // If this identifier isn't valid, emit an error.
            if (!IsValid) {
              error("invalid variable name in reference", Value);
            } else {
              Result << Bindings.lookup(std::string(VarStart, Pos - VarStart));
            }
            ++Pos;
            break;
          }

          // Track whether this is a valid identifier.
          if (!Lexer::isIdentifierChar(Char))
            IsValid = false;

          ++Pos;
        }
        continue;
      }

      // If this is a simple variable reference, expand it.
      if (Lexer::isSimpleIdentifierChar(Char)) {
        const char* VarStart = Pos;
        // Scan until the end of the simple identifier.
        ++Pos;
        while (Pos != End && Lexer::isSimpleIdentifierChar(*Pos))
          ++Pos;
        Result << Bindings.lookup(std::string(VarStart, Pos-VarStart));
        continue;
      }

      // Otherwise, we have an invalid '$' escape.
      error("invalid '$'-escape (literal '$' should be written as '$$')",
            Value);
      break;
    }

    return Result.str();
  }

  /// @name Parse Actions Interfaces
  /// @{

  virtual void initialize(ninja::Parser *Parser) override { }

  virtual void error(std::string Message, const Token &At) override {
    Actions.error(getCurrentFilename(), Message, At);
  }

  virtual void actOnBeginManifest(std::string Name) override { }

  virtual void actOnEndManifest() override {
    exitCurrentFile();
  }

  virtual void actOnBindingDecl(const Token& NameTok,
                                const Token& ValueTok) override {
    // Extract the name string.
    std::string Name(NameTok.Start, NameTok.Length);

    // Evaluate the value string with the current top-level bindings.
    std::string Value(evalString(ValueTok, getCurrentBindings()));

    getCurrentBindings().insert(Name, Value);
  }

  virtual void actOnDefaultDecl(const std::vector<Token>& NameToks) override {
    // Resolve all of the inputs and outputs.
    for (auto& NameTok: NameToks) {
      std::string Name(NameTok.Start, NameTok.Length);

      auto it = TheManifest->getNodes().find(Name);
      if (it == TheManifest->getNodes().end()) {
        error("unknown target name", NameTok);
        continue;
      }

      TheManifest->getDefaultTargets().push_back(it->second.get());
    }
  }

  virtual void actOnIncludeDecl(bool IsInclude,
                                const Token& PathTok) override {
    std::string Path = evalString(PathTok, getCurrentBindings());

    // Enter the new file.
    //
    // FIXME: Need to handle proper changes to the binding scope.
    if (enterFile(Path, getCurrentBindings(), &PathTok)) {
      // Run the parser for the included file.
      getCurrentParser()->parse();
    }
  }

  virtual BuildResult
  actOnBeginBuildDecl(const Token& NameTok,
                      const std::vector<Token> &OutputTokens,
                      const std::vector<Token> &InputTokens,
                      unsigned NumExplicitInputs,
                      unsigned NumImplicitInputs) override {
    std::string Name(NameTok.Start, NameTok.Length);

    // Resolve the rule.
    auto it = TheManifest->getRules().find(Name);
    Rule *Rule;
    if (it == TheManifest->getRules().end()) {
      error("unknown rule", NameTok);

      // Ensure we always have a rule for each command.
      Rule = TheManifest->getPhonyRule();
    } else {
      Rule = it->second.get();
    }

    // Resolve all of the inputs and outputs.
    std::vector<Node*> Outputs;
    std::vector<Node*> Inputs;
    for (auto& Token: OutputTokens) {
      // Evaluate the token string.
      std::string Path = evalString(Token, getCurrentBindings());
      Outputs.push_back(TheManifest->getOrCreateNode(Path));
    }
    for (auto& Token: InputTokens) {
      // Evaluate the token string.
      std::string Path = evalString(Token, getCurrentBindings());
      Inputs.push_back(TheManifest->getOrCreateNode(Path));
    }

    Command *Decl = new Command(Rule, Outputs, Inputs, NumExplicitInputs,
                                   NumImplicitInputs);
    TheManifest->getCommands().push_back(std::unique_ptr<Command>(Decl));

    return Decl;
  }

  virtual void actOnBuildBindingDecl(BuildResult AbstractDecl,
                                     const Token& NameTok,
                                     const Token& ValueTok) override {
    Command* Decl = static_cast<Command*>(AbstractDecl);

    std::string Name(NameTok.Start, NameTok.Length);

    // FIXME: It probably should be an error to assign to the same parameter
    // multiple times, but Ninja doesn't diagnose this.

    // The value in a build decl is always evaluated immediately, but only in
    // the context of the top-level bindings.
    //
    // FIXME: This needs to be the inherited bindings, once we support includes.
    Decl->getParameters()[Name] = evalString(ValueTok,
                                             getCurrentBindings());
  }

  virtual void actOnEndBuildDecl(PoolResult Decl,
                                const Token& StartTok) override { }

  virtual PoolResult actOnBeginPoolDecl(const Token& NameTok) override {
    std::string Name(NameTok.Start, NameTok.Length);

    // Find the hash slot.
    auto& Result = TheManifest->getPools()[Name];

    // Diagnose if the pool already exists (we still create a new one).
    if (Result.get()) {
      // The ppol already exists.
      error("duplicate pool", NameTok);
    }

    // Insert the new pool.
    Pool* Decl = new Pool(Name);
    Result.reset(Decl);
    return static_cast<PoolResult>(Decl);
  }

  virtual void actOnPoolBindingDecl(PoolResult AbstractDecl,
                                    const Token& NameTok,
                                    const Token& ValueTok) override {
    Pool* Decl = static_cast<Pool*>(AbstractDecl);

    std::string Name(NameTok.Start, NameTok.Length);

    // Evaluate the value string with the current top-level bindings.
    std::string Value(evalString(ValueTok, getCurrentBindings()));

    if (Name == "depth") {
      const char* Start = Value.c_str();
      char* End;
      long IntValue = ::strtol(Start, &End, 10);
      if (*End != '\0' || IntValue <= 0) {
        error("invalid depth", ValueTok);
      } else {
        Decl->setDepth(static_cast<uint32_t>(IntValue));
      }
    } else {
      error("unexpected variable", NameTok);
    }
  }

  virtual void actOnEndPoolDecl(PoolResult AbstractDecl,
                                const Token& StartTok) override {
    Pool* Decl = static_cast<Pool*>(AbstractDecl);

    // It is an error to not specify the pool depth.
    if (Decl->getDepth() == 0) {
      error("missing 'depth' variable assignment", StartTok);
    }
  }

  virtual RuleResult actOnBeginRuleDecl(const Token& NameTok) override {
    std::string Name(NameTok.Start, NameTok.Length);

    // Find the hash slot.
    auto& Result = TheManifest->getRules()[Name];

    // Diagnose if the rule already exists (we still create a new one).
    if (Result.get()) {
      // The rule already exists.
      error("duplicate rule", NameTok);
    }

    // Insert the new rule.
    Rule* Decl = new Rule(Name);
    Result.reset(Decl);
    return static_cast<RuleResult>(Decl);
  }

  virtual void actOnRuleBindingDecl(RuleResult AbstractDecl,
                                    const Token& NameTok,
                                    const Token& ValueTok) override {
    Rule* Decl = static_cast<Rule*>(AbstractDecl);

    std::string Name(NameTok.Start, NameTok.Length);
    // FIXME: It probably should be an error to assign to the same parameter
    // multiple times, but Ninja doesn't diagnose this.
    if (Rule::isValidParameterName(Name)) {
      Decl->getParameters()[Name] = std::string(ValueTok.Start,
                                                ValueTok.Length);
    } else {
      error("unexpected variable", NameTok);
    }
  }

  virtual void actOnEndRuleDecl(RuleResult AbstractDecl,
                                const Token& StartTok) override {
    Rule* Decl = static_cast<Rule*>(AbstractDecl);

    if (!Decl->getParameters().count("command")) {
      error("missing 'command' variable assignment", StartTok);
    }
  }

  /// @}
};

}

#pragma mark - ManifestLoader

ManifestLoader::ManifestLoader(std::string Filename,
                               ManifestLoaderActions &Actions)
  : Impl(static_cast<void*>(new ManifestLoaderImpl(Filename, Actions)))
{
}

ManifestLoader::~ManifestLoader() {
  delete static_cast<ManifestLoaderImpl*>(Impl);
}

std::unique_ptr<Manifest> ManifestLoader::load() {
  // Initialize the actions.
  static_cast<ManifestLoaderImpl*>(Impl)->getActions().initialize(this);

  return static_cast<ManifestLoaderImpl*>(Impl)->load();
}

const Parser* ManifestLoader::getCurrentParser() const {
  return static_cast<const ManifestLoaderImpl*>(Impl)->getCurrentParser();
}

<|endoftext|>


<commit_before>#include <iostream>
#include <unordered_map>
#include <string>

// This program generates an mconfig.h file. It is used in the build process.

// Map of variable name to value. Variables are passed via command line and stored
// in this map.
std::unordered_map<std::string, std::string> vars;

char to_hex_digit(int i)
{
    if (i < 10) return i + '0';
    return i - 10 + 'A';
}

// turn a string into a C++-source string, eg:
//     he said "hello"
//  becomes
//    "he said \"hello\""
static std::string stringify(std::string a)
{
    std::string out = "\"";

    for (std::string::size_type i = 0; i < a.length(); i++) {
        char c = a[i];
        if (c == '\n') out += "\\n";
        else if (c == '\t') out += "\\t";
        else if (c == '\"') out += "\\\"";
        else if (c < 0x20) {
            out += "\\x" ;
            out += to_hex_digit((c & 0xF0) >> 4);
            out += to_hex_digit((c & 0x0F));
        }
        else out += c;
    }

    out += "\"";
    return out;
}

// parse a NAME=VALUE argument and store in the variable map
void parse_arg(std::string arg)
{
    auto idx = arg.find("=", 0, 1);
    if (idx == std::string::npos) {
        throw std::string("Couldn't parse argument: ") + arg;
    }

    auto name = arg.substr(0, idx);
    auto value = arg.substr(idx + 1);
    vars.emplace(std::move(name), std::move(value));
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++) {
        parse_arg(argv[i]);
    }

    using namespace std;
    cout << "// This file is auto-generated by mconfig-gen.cc." << endl;
    cout << "const static char SYSCONTROLSOCKET[] = " << stringify(vars["SYSCONTROLSOCKET"]) << ";" << endl;
    cout << "const static char SBINDIR[] = " << stringify(vars["SBINDIR"]) << ";" << endl;
    return 0;
}
<commit_msg>mconfig-gen: generate constexpr declarations.<commit_after>#include <iostream>
#include <unordered_map>
#include <string>

// This program generates an mconfig.h file. It is used in the build process.

// Map of variable name to value. Variables are passed via command line and stored
// in this map.
std::unordered_map<std::string, std::string> vars;

char to_hex_digit(int i)
{
    if (i < 10) return i + '0';
    return i - 10 + 'A';
}

// turn a string into a C++-source string, eg:
//     he said "hello"
//  becomes
//    "he said \"hello\""
static std::string stringify(std::string a)
{
    std::string out = "\"";

    for (std::string::size_type i = 0; i < a.length(); i++) {
        char c = a[i];
        if (c == '\n') out += "\\n";
        else if (c == '\t') out += "\\t";
        else if (c == '\"') out += "\\\"";
        else if (c < 0x20) {
            out += "\\x" ;
            out += to_hex_digit((c & 0xF0) >> 4);
            out += to_hex_digit((c & 0x0F));
        }
        else out += c;
    }

    out += "\"";
    return out;
}

// parse a NAME=VALUE argument and store in the variable map
void parse_arg(std::string arg)
{
    auto idx = arg.find("=", 0, 1);
    if (idx == std::string::npos) {
        throw std::string("Couldn't parse argument: ") + arg;
    }

    auto name = arg.substr(0, idx);
    auto value = arg.substr(idx + 1);
    vars.emplace(std::move(name), std::move(value));
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++) {
        parse_arg(argv[i]);
    }

    using namespace std;
    cout << "// This file is auto-generated by mconfig-gen.cc." << endl;
    cout << "constexpr static char SYSCONTROLSOCKET[] = " << stringify(vars["SYSCONTROLSOCKET"]) << ";" << endl;
    cout << "constexpr static char SBINDIR[] = " << stringify(vars["SBINDIR"]) << ";" << endl;
    return 0;
}
<|endoftext|>