summaryrefslogtreecommitdiffhomepage
path: root/LanguageSettings.cpp
blob: 9f3feaf484b597ac19348d80adccb480a58e16f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "LanguageSettings.h"

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <numeric>
#include <stdexcept>
#include <string>
#include <vector>

#include <fmt/format.h>

#include <libreichwein/stringhelper.h>

#include "env.h"
#include "file.h"

namespace fs = std::filesystem;
using namespace std::string_literals;

LanguageSettings::LanguageSettings():
 // C++
 CXX{env_value("CXX")},
 CXXFLAGS{env_value("CXXFLAGS")},
 LDFLAGS{env_value("LDFLAGS")},
 LDLIBS{env_value("LDLIBS")},
 LIBS{env_value("LIBS")},
 // C
 // CFLAGS

 AR{env_value("AR")}
{
 std::string _default_compiler(find_executable({"g++", "clang++"}));

 if (_default_compiler.empty()) {
  throw std::runtime_error("No default C++ compiler found.");
 }

 // set defaults
 if (CXX.empty()) {
  CXX = _default_compiler;
 }

 if (CXXFLAGS.empty()) {
  CXXFLAGS = "-std=c++17 -Wall -O2";
 }

 if (AR.empty()) {
  AR = "ar";
 }
}

std::string LanguageSettings::getCompileCommand(const std::filesystem::path& target,
                                                const std::filesystem::path& source,
                                                const std::filesystem::path& build,
                                                const std::vector<std::filesystem::path>& includepaths) const
{
 std::string includes{std::accumulate(includepaths.begin(), includepaths.end(), std::string{},
                                      [](const std::string& sum, const fs::path& p){ return sum + " -I" + p.string();})};

 std::string CXXFLAGS_add{is_dynamic_lib(build) ? " -fvisibility=hidden -fPIC"s : ""s};

 // compile: $(CXX) $(CXXFLAGS) -c $< -o $@
 return fmt::format("{}{}{}{} -c {} -o {}",
                    CXX,
                    CXXFLAGS.empty() ? ""s : (" "s + CXXFLAGS),
                    CXXFLAGS_add,
                    includes,
                    source.string(),
                    target.string());
}

std::string LanguageSettings::getLinkCommand(const std::filesystem::path& target,
                                             const std::vector<std::filesystem::path> &inputs,
                                             const std::vector<std::filesystem::path>& link_libs) const
{
 std::string input_string{std::accumulate(inputs.begin(), inputs.end(), std::string{},
                                          [](const std::string& sum, const fs::path& p){
                                            if (is_dynamic_lib(p) || is_dynamic_lib_link(p)) { return sum; }
                                            return sum + " " + p.string();
                                          })};

 if (is_static_lib(target)) {
  // link: $(AR) rcs libxyz.a x.o y.o z.o
  return fmt::format("{} rcs {}{}",
                     AR,
                     target.string(),
                     input_string);
 } else if (is_executable_target(target) || is_dynamic_lib(target)) {
  // dynamic link: -shared -Wl,-soname,libXXX.so.N -o libXXX.so.N.M.O
  std::string LDFLAGS_add{is_dynamic_lib(target) ? fmt::format(" -shared -Wl,-soname,{}",
                                                               soname_shorter(target.string()).string()) : ""s};

  std::string link_libs_string;
  for (const auto& i: link_libs) {
   if (is_static_lib(i.string())) {
    input_string += " " + i.string();
   } else if (is_dynamic_lib(i.string()) || is_dynamic_lib_link(i.string())) {
    input_string += " -L" + lib_path(i).string() + " -l" + dynamic_lib_name(i);
   } else { // link external dynamic lib, e.g. "fmt"
    link_libs_string += " -l" + i.string();
   }
  }

  // link: $(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@
  return fmt::format("{}{}{}{}{}{}{} -o {}",
                     CXX,
                     LDFLAGS.empty() ? ""s : (" "s + LDFLAGS),
                     LDFLAGS_add,
                     input_string,
                     LDLIBS.empty() ? ""s : (" "s + LDLIBS),
                     LIBS.empty() ? ""s : (" "s + LIBS),
                     link_libs_string,
                     target.string());
 } else if (is_dynamic_lib_link(target)) {
  // special case for dynamic libs: add links
  if (inputs.size() != 1) {
   throw std::runtime_error("For symlink "s + target.string() + ", can link to exactly one file, found "s + std::to_string(inputs.size()));
  }
  return fmt::format("ln -sf {} {}", inputs[0].filename().string(), target.string());
 } else {
  throw std::runtime_error("No implementation for target type: "s + target.string());
 }
}

std::string LanguageSettings::getDepCommand(const std::filesystem::path& target, const std::filesystem::path &source) const
{
 // g++ -MM -MF <depfile> -c <cppfile>
 return fmt::format("{} -MM -MF {} -c {}", CXX, target.string(), source.string());
}

// TODO:
// variables:
//  CC
//  CFLAGS
//  ARFLAGS
//  AS
//  ASFLAGS