cmGlobalGhsMultiGenerator.cxx 15.1 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmGlobalGhsMultiGenerator.h"
Brad King's avatar
Brad King committed
4

5
#include "cmsys/SystemTools.hxx"
6 7 8

#include "cmAlgorithms.h"
#include "cmDocumentationEntry.h"
9
#include "cmGeneratedFileStream.h"
10
#include "cmGeneratorTarget.h"
11
#include "cmGhsMultiTargetGenerator.h"
12 13
#include "cmLocalGhsMultiGenerator.h"
#include "cmMakefile.h"
14
#include "cmState.h"
15
#include "cmVersion.h"
16
#include "cmake.h"
17

18
const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj";
19 20
const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe";
const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "C:/ghs";
21

22
cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm)
23
  : cmGlobalGenerator(cm)
24
{
25
  cm->GetState()->SetGhsMultiIDE(true);
26 27 28 29 30 31
}

cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator()
{
}

32 33
cmLocalGenerator* cmGlobalGhsMultiGenerator::CreateLocalGenerator(
  cmMakefile* mf)
34
{
35
  return new cmLocalGhsMultiGenerator(this, mf);
36 37
}

38
void cmGlobalGhsMultiGenerator::GetDocumentation(cmDocumentationEntry& entry)
39 40 41 42 43 44
{
  entry.Name = GetActualName();
  entry.Brief =
    "Generates Green Hills MULTI files (experimental, work-in-progress).";
}

45 46 47 48 49 50 51 52 53 54 55 56
void cmGlobalGhsMultiGenerator::ComputeTargetObjectDirectory(
  cmGeneratorTarget* gt) const
{
  // Compute full path to object file directory for this target.
  std::string dir;
  dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
  dir += "/";
  dir += gt->LocalGenerator->GetTargetDirectory(gt);
  dir += "/";
  gt->ObjectDirectory = dir;
}

57 58 59
bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts,
                                                    cmMakefile* mf)
{
Fred Baksik's avatar
Fred Baksik committed
60
  std::string tsp; /* toolset path */
61

Fred Baksik's avatar
Fred Baksik committed
62
  this->GetToolset(mf, tsp, ts);
63 64

  /* no toolset was found */
Fred Baksik's avatar
Fred Baksik committed
65
  if (tsp.empty()) {
66 67 68 69 70
    return false;
  } else if (ts.empty()) {
    std::string message;
    message =
      "Green Hills MULTI: -T <toolset> not specified; defaulting to \"";
Fred Baksik's avatar
Fred Baksik committed
71
    message += tsp;
72 73 74
    message += "\"";
    cmSystemTools::Message(message.c_str());

Fred Baksik's avatar
Fred Baksik committed
75
    /* store the full toolset for later use
76 77
     * -- already done if -T<toolset> was specified
     */
Fred Baksik's avatar
Fred Baksik committed
78 79
    mf->AddCacheDefinition("CMAKE_GENERATOR_TOOLSET", tsp.c_str(),
                           "Location of generator toolset.",
80 81 82 83
                           cmStateEnums::INTERNAL);
  }

  /* set the build tool to use */
Fred Baksik's avatar
Fred Baksik committed
84 85
  std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") +
                     DEFAULT_BUILD_PROGRAM);
86 87 88 89
  const char* prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");

  /* check if the toolset changed from last generate */
  if (prevTool != NULL && (gbuild != prevTool)) {
Fred Baksik's avatar
Fred Baksik committed
90
    std::string message = "toolset build tool: ";
91
    message += gbuild;
Fred Baksik's avatar
Fred Baksik committed
92
    message += "\nDoes not match the previously used build tool: ";
93 94 95 96
    message += prevTool;
    message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
               "directory or choose a different binary directory.";
    cmSystemTools::Error(message.c_str());
Fred Baksik's avatar
Fred Baksik committed
97
    return false;
98 99 100 101 102 103 104
  } else {
    /* store the toolset that is being used for this build */
    mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild.c_str(),
                           "build program to use", cmStateEnums::INTERNAL,
                           true);
  }

Fred Baksik's avatar
Fred Baksik committed
105
  mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp.c_str());
106 107 108 109

  return true;
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p,
                                                     cmMakefile* mf)
{
  if (p == "") {
    cmSystemTools::Message(
      "Green Hills MULTI: -A <arch> not specified; defaulting to \"arm\"");
    std::string arch = "arm";

    /* store the platform name for later use
     * -- already done if -A<arch> was specified
     */
    mf->AddCacheDefinition("CMAKE_GENERATOR_PLATFORM", arch.c_str(),
                           "Name of generator platform.",
                           cmStateEnums::INTERNAL);
  }

126 127
  const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM");
  if (tgtPlatform == nullptr) {
128 129
    cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not "
                           "specified; defaulting to \"integrity\"");
130 131 132 133 134 135 136 137
    tgtPlatform = "integrity";
  }

  /* store the platform name for later use */
  mf->AddCacheDefinition("GHS_TARGET_PLATFORM", tgtPlatform,
                         "Name of GHS target platform.",
                         cmStateEnums::INTERNAL);

138 139 140
  return true;
}

141
void cmGlobalGhsMultiGenerator::EnableLanguage(
142
  std::vector<std::string> const& l, cmMakefile* mf, bool optional)
143 144 145 146 147 148 149
{
  mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI");

  mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files
  this->cmGlobalGenerator::EnableLanguage(l, mf, optional);
}

150
bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/)
151
{
152 153 154 155
  // The GHS generator only knows how to lookup its build tool
  // during generation of the project files, but this
  // can only be done after the toolset is specified.

156
  return true;
157 158
}

159
void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsd,
Fred Baksik's avatar
Fred Baksik committed
160
                                           const std::string& ts)
161
{
162
  const char* ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT");
163

Fred Baksik's avatar
Fred Baksik committed
164
  if (!ghsRoot || ghsRoot[0] == '\0') {
165
    ghsRoot = DEFAULT_TOOLSET_ROOT;
166
  }
167
  tsd = ghsRoot;
168

169 170
  if (ts.empty()) {
    std::vector<std::string> output;
171

172
    // Use latest? version
Fred Baksik's avatar
Fred Baksik committed
173 174 175
    if (tsd.back() != '/') {
      tsd += "/";
    }
176
    cmSystemTools::Glob(tsd, "comp_[^;]+", output);
177

178
    if (output.empty()) {
Fred Baksik's avatar
Fred Baksik committed
179 180 181 182
      std::string msg =
        "No GHS toolsets found in GHS_TOOLSET_ROOT \"" + tsd + "\".";
      cmSystemTools::Error(msg.c_str());
      tsd = "";
183
    } else {
Fred Baksik's avatar
Fred Baksik committed
184
      tsd += output.back();
185 186
    }
  } else {
Fred Baksik's avatar
Fred Baksik committed
187 188 189
    std::string tryPath;
    /* CollapseCombinedPath will check if ts is an absolute path */
    tryPath = cmSystemTools::CollapseCombinedPath(tsd, ts);
190
    if (!cmSystemTools::FileExists(tryPath)) {
Fred Baksik's avatar
Fred Baksik committed
191 192 193 194 195
      std::string msg = "GHS toolset \"" + tryPath + "\" not found.";
      cmSystemTools::Error(msg.c_str());
      tsd = "";
    } else {
      tsd = tryPath;
196
    }
197
  }
198 199
}

200
void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout)
201 202
{
  fout << "#!gbuild" << std::endl;
203 204 205 206 207 208 209
  fout << "#" << std::endl
       << "# CMAKE generated file: DO NOT EDIT!" << std::endl
       << "# Generated by \"" << this->GetActualName() << "\""
       << " Generator, CMake Version " << cmVersion::GetMajorVersion() << "."
       << cmVersion::GetMinorVersion() << std::endl
       << "#" << std::endl
       << std::endl;
210
}
211

212 213 214
void cmGlobalGhsMultiGenerator::WriteTopLevelProject(
  std::ostream& fout, cmLocalGenerator* root,
  std::vector<cmLocalGenerator*>& generators)
215
{
216
  WriteFileHeader(fout);
217 218 219

  this->WriteMacros(fout);
  this->WriteHighLevelDirectives(fout);
220 221
  GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);

222
  fout << "# Top Level Project File" << std::endl;
223 224 225 226 227 228 229 230

  // Specify BSP option if supplied by user
  // -- not all platforms require this entry in the project file
  //    integrity platforms require this field; use default if needed
  std::string platform;
  if (const char* p =
        this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM")) {
    platform = p;
231
  }
232

Fred Baksik's avatar
Fred Baksik committed
233
  std::string bspName;
234 235
  if (char const* bspCache =
        this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME")) {
Fred Baksik's avatar
Fred Baksik committed
236
    bspName = bspCache;
237
    this->GetCMakeInstance()->MarkCliAsUsed("GHS_BSP_NAME");
238 239
  } else {
    bspName = "IGNORE";
240
  }
241 242 243

  if (platform.find("integrity") != std::string::npos &&
      cmSystemTools::IsOff(bspName.c_str())) {
Fred Baksik's avatar
Fred Baksik committed
244 245 246 247 248 249
    const char* a =
      this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM");
    bspName = "sim";
    bspName += (a ? a : "");
  }

250
  if (!cmSystemTools::IsOff(bspName.c_str())) {
251
    fout << "    -bsp " << bspName << std::endl;
252
  }
253

254 255 256 257 258 259 260 261 262 263 264 265 266
  // Specify OS DIR if supplied by user
  // -- not all platforms require this entry in the project file
  std::string osDir;
  std::string osDirOption;
  if (char const* osDirCache =
        this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR")) {
    osDir = osDirCache;
  }

  if (char const* osDirOptionCache =
        this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION")) {
    osDirOption = osDirOptionCache;
  }
Fred Baksik's avatar
Fred Baksik committed
267

268 269 270
  if (!cmSystemTools::IsOff(osDir.c_str()) ||
      platform.find("integrity") != std::string::npos) {
    std::replace(osDir.begin(), osDir.end(), '\\', '/');
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
    fout << "    " << osDirOption << "\"" << osDir << "\"" << std::endl;
  }

  WriteSubProjects(fout, root, generators);
}

void cmGlobalGhsMultiGenerator::WriteSubProjects(
  std::ostream& fout, cmLocalGenerator* root,
  std::vector<cmLocalGenerator*>& generators)
{
  // Collect all targets under this root generator and the transitive
  // closure of their dependencies.
  TargetDependSet projectTargets;
  TargetDependSet originalTargets;
  this->GetTargetSets(projectTargets, originalTargets, root, generators);
  OrderedTargetDependSet orderedProjectTargets(projectTargets, "");

  // write out all the sub-projects
  std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
  for (cmGeneratorTarget const* target : orderedProjectTargets) {
    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
      continue;
    }

    const char* projName = target->GetProperty("GENERATOR_FILE_NAME");
    const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
    if (projName && projType) {
      cmLocalGenerator* lg = target->GetLocalGenerator();
      std::string dir = lg->GetCurrentBinaryDirectory();
      dir = root->ConvertToRelativePath(rootBinaryDir, dir.c_str());
      if (dir == ".") {
        dir.clear();
      } else {
        if (dir.back() != '/') {
          dir += "/";
        }
      }
Fred Baksik's avatar
Fred Baksik committed
308 309 310 311

      if (cmSystemTools::IsOn(target->GetProperty("EXCLUDE_FROM_ALL"))) {
        fout << "{comment} ";
      }
312 313
      std::string projFile = dir + projName + FILE_EXTENSION;
      fout << projFile;
314
      fout << " " << projType << std::endl;
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

      if (cmSystemTools::IsOn(target->GetProperty("GHS_REFERENCE_PROJECT"))) {
        // create reference project
        std::string fname = dir;
        fname += target->GetName();
        fname += "REF";
        fname += FILE_EXTENSION;

        cmGeneratedFileStream fref(fname.c_str());
        fref.SetCopyIfDifferent(true);

        this->WriteFileHeader(fref);
        GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fref);
        fref << "    :reference=" << projFile << std::endl;

        fref.Close();
      }
332
    }
333
  }
334 335 336 337
}

void cmGlobalGhsMultiGenerator::Generate()
{
338
  // first do the superclass method
339 340
  this->cmGlobalGenerator::Generate();

341 342 343 344 345
  // output top-level projects
  for (auto& it : this->ProjectMap) {
    this->OutputTopLevelProject(it.second[0], it.second);
  }
}
346

347 348 349 350 351
void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
  cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
{
  if (generators.empty()) {
    return;
352
  }
353

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
  /* Name top-level projects as filename.top.gpj to avoid name clashes
   * with target projects.  This avoid the issue where the project has
   * the same name as the executable target.
   */
  std::string fname = root->GetCurrentBinaryDirectory();
  fname += "/";
  fname += root->GetProjectName();
  fname += ".top";
  fname += FILE_EXTENSION;

  cmGeneratedFileStream fout(fname.c_str());
  fout.SetCopyIfDifferent(true);

  this->WriteTopLevelProject(fout, root, generators);

  fout.Close();
370 371 372
}

void cmGlobalGhsMultiGenerator::GenerateBuildCommand(
373
  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
374
  const std::string& projectName, const std::string& projectDir,
375
  const std::string& targetName, const std::string& /*config*/, bool /*fast*/,
376
  int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions)
377
{
378 379
  const char* gbuild =
    this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
380
  makeCommand.add(this->SelectMakeProgram(makeProgram, (std::string)gbuild));
381

382
  if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
383
    makeCommand.add("-parallel");
384
    if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
385
      makeCommand.add(std::to_string(jobs));
386 387 388
    }
  }

389
  makeCommand.add(makeOptions.begin(), makeOptions.end());
390 391 392 393 394 395 396 397 398 399 400 401

  /* determine which top-project file to use */
  std::string proj = projectName + ".top" + FILE_EXTENSION;
  std::vector<std::string> files;
  cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
  if (!files.empty()) {
    auto p = std::find(files.begin(), files.end(), proj);
    if (p == files.end()) {
      proj = files.at(0);
    }
  }

402
  makeCommand.add("-top", proj);
403 404
  if (!targetName.empty()) {
    if (targetName == "clean") {
405
      makeCommand.add("-clean");
406
    } else {
407
      if (targetName.compare(targetName.size() - 4, 4, ".gpj") == 0) {
408
        makeCommand.add(targetName);
409
      } else {
410
        makeCommand.add(targetName + ".gpj");
411
      }
412
    }
413
  }
414 415
}

416
void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout)
417
{
418
  char const* ghsGpjMacros =
419
    this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS");
420
  if (NULL != ghsGpjMacros) {
421 422 423 424
    std::vector<std::string> expandedList;
    cmSystemTools::ExpandListArgument(std::string(ghsGpjMacros), expandedList);
    for (std::vector<std::string>::const_iterator expandedListI =
           expandedList.begin();
425
         expandedListI != expandedList.end(); ++expandedListI) {
426
      fout << "macro " << *expandedListI << std::endl;
427
    }
428
  }
429 430
}

431
void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout)
432
{
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
  /* set primary target */
  std::string tgt;
  const char* t =
    this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET");
  if (t) {
    tgt = t;
    this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET");
  } else {
    const char* a =
      this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM");
    const char* p =
      this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM");
    tgt = (a ? a : "");
    tgt += "_";
    tgt += (p ? p : "");
    tgt += ".tgt";
  }

451
  fout << "primaryTarget=" << tgt << std::endl;
452

453
  char const* const customization =
454
    this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION");
455
  if (NULL != customization && strlen(customization) > 0) {
456
    fout << "customization=" << trimQuotes(customization) << std::endl;
457
    this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION");
458
  }
459 460
}

461
std::string cmGlobalGhsMultiGenerator::trimQuotes(std::string const& str)
462 463 464
{
  std::string result;
  result.reserve(str.size());
465 466
  for (const char* ch = str.c_str(); *ch != '\0'; ++ch) {
    if (*ch != '"') {
467 468
      result += *ch;
    }
469
  }
470 471
  return result;
}
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493

bool cmGlobalGhsMultiGenerator::TargetCompare::operator()(
  cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
{
  // Make sure a given named target is ordered first,
  // e.g. to set ALL_BUILD as the default active project.
  // When the empty string is named this is a no-op.
  if (r->GetName() == this->First) {
    return false;
  }
  if (l->GetName() == this->First) {
    return true;
  }
  return l->GetName() < r->GetName();
}

cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  TargetDependSet const& targets, std::string const& first)
  : derived(TargetCompare(first))
{
  this->insert(targets.begin(), targets.end());
}