Skip to content

Commit 6e4c674

Browse files
authored
feat!: support recursive path build (#1260)
1 parent 81c2676 commit 6e4c674

File tree

17 files changed

+327
-33
lines changed

17 files changed

+327
-33
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ COPY Makefile .
1919
COPY include ./include/
2020
COPY lib ./lib/
2121
COPY src ./src/
22+
COPY semver ./semver/
2223
RUN make BUILD=release install
2324

2425
FROM $base AS runtime

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ DEFINES := -DCABIN_CABIN_PKG_VERSION='"$(VERSION)"' \
4444
-DCABIN_CABIN_COMMIT_HASH='"$(COMMIT_HASH)"' \
4545
-DCABIN_CABIN_COMMIT_SHORT_HASH='"$(COMMIT_SHORT_HASH)"' \
4646
-DCABIN_CABIN_COMMIT_DATE='"$(COMMIT_DATE)"'
47-
INCLUDES := -Iinclude -Isrc -isystem $(O)/DEPS/toml11/include \
47+
INCLUDES := -Iinclude -Isrc -Isemver/include -isystem $(O)/DEPS/toml11/include \
4848
-isystem $(O)/DEPS/mitama-cpp-result/include \
4949
-isystem $(O)/DEPS/rs-cpp/include
5050

@@ -63,7 +63,8 @@ endif
6363
LDLIBS := $(PKG_LIBS)
6464

6565
# Source files
66-
SRCS := $(shell find src -name '*.cc') $(shell find lib -name '*.cc')
66+
SRCS := $(shell find src -name '*.cc') $(shell find lib -name '*.cc') \
67+
$(shell find semver/lib -name '*.cc')
6768
OBJS := $(SRCS:%.cc=$(O)/%.o)
6869
DEPS := $(OBJS:.o=.d)
6970

cabin.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ libgit2 = {version = ">=1.7 && <1.10", system = true}
1919
nlohmann_json = {version = "3.10.5", system = true}
2020
tbb = {version = ">=2021.5.0 && <2023.0.0", system = true}
2121
rs-cpp = {git = "https://github.com/ken-matsui/rs-cpp.git", branch = "main"}
22+
semver = {path = "semver"}
2223

2324
[dev-dependencies]
2425
boost-ut = {git = "https://github.com/boost-ext/ut.git", tag = "v2.3.1"}

include/Builder/BuildGraph.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class BuildGraph {
4646
const std::string& libraryName() const { return libName; }
4747
const std::vector<TestTarget>& testTargets() const { return testTargets_; }
4848

49-
rs::Result<void> installDeps(bool includeDevDeps);
49+
rs::Result<void> installDeps(bool includeDevDeps, bool suppressDepDiag);
5050
void enableCoverage();
5151
rs::Result<void> plan(bool logAnalysis = true);
5252
rs::Result<void> writeBuildFilesIfNeeded() const;
@@ -123,6 +123,7 @@ class BuildGraph {
123123
std::unordered_map<std::string, CompileUnit> compileUnits;
124124
std::vector<TestTarget> testTargets_;
125125
std::unordered_set<std::string> srcObjectTargets;
126+
std::unordered_set<std::string> libSupportObjects;
126127
std::string archiver = "ar";
127128

128129
std::string cxxFlags;

include/Manifest.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ class Manifest {
129129
static rs::Result<fs::path>
130130
findPath(fs::path candidateDir = fs::current_path()) noexcept;
131131

132-
rs::Result<std::vector<CompilerOpts>> installDeps(bool includeDevDeps) const;
132+
rs::Result<std::vector<CompilerOpts>>
133+
installDeps(bool includeDevDeps, const BuildProfile& buildProfile,
134+
bool suppressDepDiag = false) const;
133135

134136
private:
135137
Manifest(fs::path path, Package package, std::vector<Dependency> dependencies,

lib/Builder/BuildGraph.cc

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,11 @@ bool BuildGraph::isUpToDate(const std::string_view fileName) const {
128128
}
129129

130130
const fs::file_time_type configTime = fs::last_write_time(filePath);
131-
const std::array<fs::path, 3> watchedDirs{
131+
const std::array<fs::path, 4> watchedDirs{
132132
project.rootPath / "src",
133133
project.rootPath / "lib",
134134
project.rootPath / "include",
135+
project.rootPath / "tests",
135136
};
136137
for (const fs::path& dir : watchedDirs) {
137138
if (!fs::exists(dir)) {
@@ -432,6 +433,10 @@ BuildGraph::processUnittestSrc(const fs::path& sourceFilePath,
432433

433434
std::vector<std::string> linkInputs;
434435
linkInputs.push_back(testObjTarget);
436+
std::unordered_set<std::string> linkSeen(linkInputs.begin(),
437+
linkInputs.end());
438+
439+
const std::string underTest = sourceFilePath.stem().string();
435440

436441
if (isSrcUnit) {
437442
std::unordered_set<std::string> deps;
@@ -441,6 +446,16 @@ BuildGraph::processUnittestSrc(const fs::path& sourceFilePath,
441446
std::vector<std::string> srcDeps(deps.begin(), deps.end());
442447
std::ranges::sort(srcDeps);
443448
linkInputs.insert(linkInputs.end(), srcDeps.begin(), srcDeps.end());
449+
linkSeen.insert(srcDeps.begin(), srcDeps.end());
450+
}
451+
452+
for (const std::string& obj : libSupportObjects) {
453+
if (fs::path(obj).stem() == underTest) {
454+
continue;
455+
}
456+
if (linkSeen.insert(obj).second) {
457+
linkInputs.push_back(obj);
458+
}
444459
}
445460

446461
if (hasLibraryTarget_) {
@@ -486,10 +501,22 @@ BuildGraph::processIntegrationTestSrc(const fs::path& sourceFilePath,
486501
fs::relative(testBinaryPath, outBasePath_).generic_string();
487502

488503
std::vector<std::string> linkInputs{ testObjTarget };
504+
std::unordered_set<std::string> linkSeen(linkInputs.begin(),
505+
linkInputs.end());
506+
507+
const std::string underTest = sourceFilePath.stem().string();
508+
for (const std::string& obj : libSupportObjects) {
509+
if (fs::path(obj).stem() == underTest) {
510+
continue;
511+
}
512+
if (linkSeen.insert(obj).second) {
513+
linkInputs.push_back(obj);
514+
}
515+
}
516+
489517
if (hasLibraryTarget_) {
490518
linkInputs.push_back(libName);
491519
}
492-
std::ranges::sort(linkInputs);
493520

494521
NinjaEdge linkEdge;
495522
linkEdge.outputs = { testBinary };
@@ -547,9 +574,11 @@ void BuildGraph::collectBinDepObjs(
547574
}
548575
}
549576

550-
rs::Result<void> BuildGraph::installDeps(const bool includeDevDeps) {
577+
rs::Result<void> BuildGraph::installDeps(const bool includeDevDeps,
578+
const bool suppressDepDiag) {
551579
const std::vector<CompilerOpts> depsCompOpts =
552-
rs_try(project.manifest.installDeps(includeDevDeps));
580+
rs_try(project.manifest.installDeps(includeDevDeps, buildProfile_,
581+
suppressDepDiag));
553582

554583
for (const CompilerOpts& depOpts : depsCompOpts) {
555584
project.compilerOpts.merge(depOpts);
@@ -632,12 +661,6 @@ rs::Result<void> BuildGraph::configure() {
632661
}
633662
hasLibraryTarget_ = !publicSourceFilePaths.empty();
634663

635-
if (!hasBinaryTarget_ && !hasLibraryTarget_) {
636-
rs_bail("expected either `src/main{}` or at least one source file under "
637-
"`lib/` matching {}",
638-
SOURCE_FILE_EXTS, SOURCE_FILE_EXTS);
639-
}
640-
641664
const SourceRoot srcRoot(srcDir);
642665
const SourceRoot libRoot(libDir, fs::path("lib"));
643666

@@ -656,6 +679,19 @@ rs::Result<void> BuildGraph::configure() {
656679
std::unordered_set<std::string> buildObjTargets = srcObjTargets;
657680
buildObjTargets.insert(libObjTargets.begin(), libObjTargets.end());
658681

682+
libSupportObjects.clear();
683+
for (const std::string& libObj : libObjTargets) {
684+
const auto it = compileUnits.find(libObj);
685+
if (it == compileUnits.end()) {
686+
continue;
687+
}
688+
collectBinDepObjs(libSupportObjects, "", it->second.dependencies,
689+
buildObjTargets);
690+
}
691+
std::erase_if(libSupportObjects, [this](const std::string& obj) {
692+
return !srcObjectTargets.contains(obj);
693+
});
694+
659695
if (hasBinaryTarget_) {
660696
const fs::path mainObjPath = project.buildOutPath / "main.o";
661697
const std::string mainObj =

lib/Manifest.cc

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include "Manifest.hpp"
22

33
#include "Builder/BuildProfile.hpp"
4+
#include "Builder/Builder.hpp"
45
#include "Builder/Compiler.hpp"
6+
#include "Diag.hpp"
57
#include "Semver.hpp"
68
#include "TermColor.hpp"
79
#include "VersionReq.hpp"
@@ -103,14 +105,16 @@ static fs::path canonicalizePathDep(const fs::path& baseDir,
103105
}
104106

105107
static rs::Result<void>
106-
installDependencies(const Manifest& manifest, bool includeDevDeps,
108+
installDependencies(const Manifest& manifest, const BuildProfile& buildProfile,
109+
bool includeDevDeps, bool suppressDepDiag,
107110
std::unordered_map<std::string, DepKey>& seenDeps,
108111
std::unordered_set<fs::path>& visited,
109112
std::vector<CompilerOpts>& installed);
110113

111114
static rs::Result<void>
112115
installPathDependency(const Manifest& manifest, const PathDependency& pathDep,
113-
bool includeDevDeps,
116+
const BuildProfile& buildProfile, bool includeDevDeps,
117+
bool suppressDepDiag,
114118
std::unordered_map<std::string, DepKey>& seenDeps,
115119
std::unordered_set<fs::path>& visited,
116120
std::vector<CompilerOpts>& installed) {
@@ -124,22 +128,58 @@ installPathDependency(const Manifest& manifest, const PathDependency& pathDep,
124128
}
125129

126130
CompilerOpts pathOpts;
127-
pathOpts.cFlags.includeDirs.emplace_back(resolveIncludeDir(depRoot),
128-
/*isSystem=*/false);
129-
130131
const fs::path depManifestPath = depRoot / Manifest::FILE_NAME;
131132
rs_ensure(fs::exists(depManifestPath), "missing `{}` in path dependency {}",
132133
Manifest::FILE_NAME, depRoot.string());
133134
const Manifest depManifest =
134135
rs_try(Manifest::tryParse(depManifestPath, false));
135136

137+
if (!suppressDepDiag) {
138+
Diag::info("Building", "{} ({})", depManifest.package.name,
139+
depRoot.string());
140+
}
141+
142+
Builder depBuilder(depRoot, buildProfile);
143+
ScheduleOptions depOptions;
144+
depOptions.includeDevDeps = includeDevDeps;
145+
depOptions.suppressAnalysisLog = true;
146+
depOptions.suppressFinishLog = true;
147+
depOptions.suppressDepDiag = suppressDepDiag;
148+
rs_try(depBuilder.schedule(depOptions));
149+
rs_try(depBuilder.build());
150+
151+
const BuildGraph& depGraph = depBuilder.graph();
152+
const fs::path depOutDir = depGraph.outBasePath();
153+
const fs::path libPath = depOutDir / depGraph.libraryName();
154+
155+
pathOpts.cFlags.includeDirs.emplace_back(resolveIncludeDir(depRoot),
156+
/*isSystem=*/false);
157+
136158
std::vector<CompilerOpts> nestedDeps;
137-
rs_try(installDependencies(depManifest, includeDevDeps, seenDeps, visited,
138-
nestedDeps));
159+
rs_try(installDependencies(depManifest, buildProfile, includeDevDeps,
160+
suppressDepDiag, seenDeps, visited, nestedDeps));
139161
for (const CompilerOpts& opts : nestedDeps) {
140162
pathOpts.merge(opts);
141163
}
142164

165+
const bool libBuilt = fs::exists(libPath);
166+
if (depGraph.hasLibraryTarget()) {
167+
rs_ensure(libBuilt, "expected `{}` to be built for dependency {}",
168+
libPath.string(), depManifest.package.name);
169+
}
170+
171+
if (libBuilt) {
172+
pathOpts.ldFlags.libDirs.emplace(pathOpts.ldFlags.libDirs.begin(),
173+
libPath.parent_path());
174+
175+
std::string libName = libPath.stem().string();
176+
if (libName.starts_with("lib")) {
177+
libName.erase(0, 3);
178+
}
179+
pathOpts.ldFlags.libs.emplace(pathOpts.ldFlags.libs.begin(),
180+
std::move(libName));
181+
}
182+
143183
installed.emplace_back(std::move(pathOpts));
144184
return rs::Ok();
145185
}
@@ -201,7 +241,8 @@ rememberDep(const Manifest& manifest, const Dependency& dep,
201241
}
202242

203243
static rs::Result<void>
204-
installDependencies(const Manifest& manifest, const bool includeDevDeps,
244+
installDependencies(const Manifest& manifest, const BuildProfile& buildProfile,
245+
const bool includeDevDeps, const bool suppressDepDiag,
205246
std::unordered_map<std::string, DepKey>& seenDeps,
206247
std::unordered_set<fs::path>& visited,
207248
std::vector<CompilerOpts>& installed) {
@@ -219,7 +260,8 @@ installDependencies(const Manifest& manifest, const bool includeDevDeps,
219260
rs_try(Manifest::tryParse(depManifestPath, false));
220261

221262
std::vector<CompilerOpts> nestedDeps;
222-
rs_try(installDependencies(depManifest, includeDevDeps,
263+
rs_try(installDependencies(depManifest, buildProfile,
264+
includeDevDeps, suppressDepDiag,
223265
seenDeps, visited, nestedDeps));
224266
for (const CompilerOpts& opts : nestedDeps) {
225267
depOpts.merge(opts);
@@ -234,7 +276,8 @@ installDependencies(const Manifest& manifest, const bool includeDevDeps,
234276
return rs::Ok();
235277
},
236278
[&](const PathDependency& pathDep) -> rs::Result<void> {
237-
return installPathDependency(manifest, pathDep, includeDevDeps,
279+
return installPathDependency(manifest, pathDep, buildProfile,
280+
includeDevDeps, suppressDepDiag,
238281
seenDeps, visited, installed);
239282
},
240283
},
@@ -663,12 +706,14 @@ rs::Result<fs::path> Manifest::findPath(fs::path candidateDir) noexcept {
663706
}
664707

665708
rs::Result<std::vector<CompilerOpts>>
666-
Manifest::installDeps(const bool includeDevDeps) const {
709+
Manifest::installDeps(const bool includeDevDeps,
710+
const BuildProfile& buildProfile,
711+
const bool suppressDepDiag) const {
667712
std::unordered_map<std::string, DepKey> seenDeps;
668713
std::unordered_set<fs::path> visited;
669714
std::vector<CompilerOpts> installed;
670-
rs_try(
671-
installDependencies(*this, includeDevDeps, seenDeps, visited, installed));
715+
rs_try(installDependencies(*this, buildProfile, includeDevDeps,
716+
suppressDepDiag, seenDeps, visited, installed));
672717
return rs::Ok(installed);
673718
}
674719

semver/cabin.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "semver"
3+
edition = "23"
4+
version = "0.1.0"
5+
6+
[dependencies]
7+
rs-cpp = { git = "https://github.com/ken-matsui/rs-cpp.git", branch = "main" }
8+
9+
[profile]
10+
cxxflags = ["-pedantic-errors", "-Wall", "-Wextra", "-Wpedantic", "-fno-rtti"]

0 commit comments

Comments
 (0)