Skip to content

Commit 7ee1538

Browse files
authored
Custom generator (#212)
1 parent afff3dd commit 7ee1538

27 files changed

+1699
-17
lines changed

bootstrap/src/build_buildcc.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ namespace buildcc {
2020

2121
void schema_gen_cb(BaseGenerator &generator, const BaseTarget &flatc_exe) {
2222
generator.AddInput("{gen_root_dir}/path.fbs", "path_fbs");
23+
generator.AddInput("{gen_root_dir}/custom_generator.fbs",
24+
"custom_generator_fbs");
2325
generator.AddInput("{gen_root_dir}/generator.fbs", "generator_fbs");
2426
generator.AddInput("{gen_root_dir}/target.fbs", "target_fbs");
2527

2628
generator.AddOutput("{gen_build_dir}/path_generated.h");
29+
generator.AddOutput("{gen_build_dir}/custom_generator_generated.h");
2730
generator.AddOutput("{gen_build_dir}/generator_generated.h");
2831
generator.AddOutput("{gen_build_dir}/target_generated.h");
2932

@@ -33,7 +36,7 @@ void schema_gen_cb(BaseGenerator &generator, const BaseTarget &flatc_exe) {
3336
// generator.AddCommand("{flatc_compiler} --help");
3437
generator.AddCommand(
3538
"{flatc_compiler} -o {gen_build_dir} -I {gen_root_dir} --gen-object-api "
36-
"--cpp {path_fbs} {generator_fbs} {target_fbs}");
39+
"--cpp {path_fbs} {custom_generator_fbs} {generator_fbs} {target_fbs}");
3740

3841
generator.Build();
3942
}
@@ -71,6 +74,7 @@ void buildcc_cb(BaseTarget &target, const BaseGenerator &schema_gen,
7174

7275
// TARGET
7376
target.GlobSources("lib/target/src/common");
77+
target.GlobSources("lib/target/src/custom_generator");
7478
target.GlobSources("lib/target/src/generator");
7579
target.GlobSources("lib/target/src/api");
7680
target.GlobSources("lib/target/src/target_info");

buildcc/buildcc.h

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
// Base
3333
#include "toolchain/toolchain.h"
34+
#include "target/custom_generator.h"
3435
#include "target/generator.h"
3536
#include "target/target_info.h"
3637
#include "target/target.h"

buildcc/lib/env/src/command.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,14 @@ std::string Command::Construct(
6161
});
6262

6363
// Construct your command
64-
return fmt::vformat(pattern, store);
64+
std::string ret;
65+
try {
66+
ret = fmt::vformat(pattern, store);
67+
} catch (const std::exception &e) {
68+
env::assert_fatal<false>(
69+
fmt::format("Construct command failed: {}", e.what()));
70+
}
71+
return ret;
6572
}
6673

6774
} // namespace buildcc::env

buildcc/lib/target/cmake/common_target_src.cmake

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ set(COMMON_TARGET_SRCS
3434
include/target/api/target_getter.h
3535

3636
# Generator
37+
src/custom_generator/custom_generator.cpp
38+
include/target/custom_generator.h
3739
src/generator/generator.cpp
3840
include/target/generator.h
3941

buildcc/lib/target/cmake/mock_target.cmake

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
add_library(mock_target STATIC
22
${COMMON_TARGET_SRCS}
3+
# Custom Generator mocks
4+
mock/custom_generator/runner.cpp
5+
mock/custom_generator/recheck_states.cpp
36

47
# Generator mocks
58
src/generator/task.cpp

buildcc/lib/target/cmake/target.cmake

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
set(TARGET_SRCS
22
${COMMON_TARGET_SRCS}
33

4+
src/custom_generator/recheck_states.cpp
5+
46
src/generator/task.cpp
57
src/generator/recheck_states.cpp
68

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright 2021-2022 Niket Naidu. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef TARGET_CUSTOM_GENERATOR_H_
18+
#define TARGET_CUSTOM_GENERATOR_H_
19+
20+
#include <mutex>
21+
#include <string>
22+
#include <unordered_map>
23+
#include <vector>
24+
25+
#include "taskflow/taskflow.hpp"
26+
27+
#include "env/command.h"
28+
#include "env/task_state.h"
29+
30+
#include "target/interface/builder_interface.h"
31+
32+
#include "schema/custom_generator_serialization.h"
33+
#include "schema/path.h"
34+
35+
#include "target/common/target_env.h"
36+
37+
namespace buildcc {
38+
39+
// TODO, Shift to a different file
40+
// TODO, Check if we need the "id" here as well
41+
class CustomGeneratorContext {
42+
public:
43+
CustomGeneratorContext(const env::Command &c, const fs_unordered_set &i,
44+
const fs_unordered_set &o)
45+
: command(c), inputs(i), outputs(o) {}
46+
47+
public:
48+
const env::Command &command;
49+
const fs_unordered_set &inputs;
50+
const fs_unordered_set &outputs;
51+
};
52+
53+
// clang-format off
54+
typedef std::function<bool(CustomGeneratorContext &)> GenerateCb;
55+
56+
typedef std::function<void(std::unordered_map<std::string, tf::Task> &)> DependencyCb;
57+
// clang-format on
58+
59+
struct UserRelInputOutputSchema : internal::RelInputOutputSchema {
60+
fs_unordered_set inputs;
61+
GenerateCb generate_cb;
62+
};
63+
64+
struct UserCustomGeneratorSchema : public internal::CustomGeneratorSchema {
65+
std::unordered_map<std::string, UserRelInputOutputSchema> rels_map;
66+
67+
void ConvertToInternal() {
68+
for (auto &r_miter : rels_map) {
69+
r_miter.second.internal_inputs = path_schema_convert(
70+
r_miter.second.inputs, internal::Path::CreateExistingPath);
71+
auto p = internal_rels_map.emplace(r_miter.first, r_miter.second);
72+
env::assert_fatal(p.second,
73+
fmt::format("Could not save {}", r_miter.first));
74+
}
75+
}
76+
};
77+
78+
class CustomGenerator : public internal::BuilderInterface {
79+
public:
80+
CustomGenerator(const std::string &name, const TargetEnv &env)
81+
: name_(name),
82+
env_(env.GetTargetRootDir(), env.GetTargetBuildDir() / name),
83+
serialization_(env_.GetTargetBuildDir() / fmt::format("{}.bin", name)) {
84+
Initialize();
85+
}
86+
virtual ~CustomGenerator() = default;
87+
CustomGenerator(const CustomGenerator &) = delete;
88+
89+
/**
90+
* @brief Add default arguments for input, output and command requirements
91+
*
92+
* @param arguments Key-Value pair for arguments
93+
*/
94+
void AddDefaultArguments(
95+
const std::unordered_map<std::string, std::string> &arguments);
96+
97+
/**
98+
* @brief Single Generator task for inputs->generate_cb->outputs
99+
*
100+
* @param id Unique id associated with Generator task
101+
* @param inputs File inputs
102+
* @param outputs File outputs
103+
* @param generate_cb User-defined generate callback to build outputs from the
104+
* provided inputs
105+
*/
106+
void AddGenInfo(const std::string &id, const fs_unordered_set &inputs,
107+
const fs_unordered_set &outputs,
108+
const GenerateCb &generate_cb);
109+
110+
// Callbacks
111+
/**
112+
* @brief Setup dependencies between Tasks using their `id`
113+
* For example: `task_map["id1"].precede(task_map["id2"])`
114+
*
115+
* IMPORTANT: Successor tasks will not automatically run if dependent task is
116+
* run.
117+
* The Dependency callback only sets precedence (order in which your tasks
118+
* should run)
119+
* Default behaviour when dependency callback is not supplied: All task `id`s
120+
* run in parallel.
121+
*
122+
* @param dependency_cb Unordered map of `id` and `task`
123+
* The map can be safely mutated.
124+
*/
125+
void AddDependencyCb(const DependencyCb &dependency_cb);
126+
127+
void Build() override;
128+
129+
// Getters
130+
const fs::path &GetBinaryPath() const {
131+
return serialization_.GetSerializedFile();
132+
}
133+
const fs::path &GetRootDir() const { return env_.GetTargetRootDir(); }
134+
const fs::path &GetBuildDir() const { return env_.GetTargetBuildDir(); }
135+
tf::Taskflow &GetTaskflow() { return tf_; }
136+
137+
private:
138+
void Initialize();
139+
140+
template <bool run> void TaskRunner(const std::string &id);
141+
142+
void GenerateTask();
143+
void BuildGenerate(std::unordered_map<std::string, UserRelInputOutputSchema>
144+
&gen_selected_map,
145+
std::unordered_map<std::string, UserRelInputOutputSchema>
146+
&dummy_gen_selected_map);
147+
148+
// Recheck states
149+
void IdRemoved();
150+
void IdAdded();
151+
void IdUpdated();
152+
153+
private:
154+
std::string name_;
155+
TargetEnv env_;
156+
internal::CustomGeneratorSerialization serialization_;
157+
158+
// Serialization
159+
UserCustomGeneratorSchema user_;
160+
161+
std::mutex success_schema_mutex_;
162+
std::unordered_map<std::string, UserRelInputOutputSchema> success_schema_;
163+
164+
// Internal
165+
env::Command command_;
166+
tf::Taskflow tf_;
167+
168+
// Callbacks
169+
DependencyCb dependency_cb_;
170+
};
171+
172+
} // namespace buildcc
173+
174+
#endif

buildcc/lib/target/include/target/generator.h

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,13 @@
1717
#ifndef TARGET_GENERATOR_H_
1818
#define TARGET_GENERATOR_H_
1919

20-
#include <functional>
2120
#include <mutex>
2221
#include <string>
2322
#include <unordered_map>
2423
#include <vector>
2524

2625
#include "taskflow/taskflow.hpp"
2726

28-
#include "env/env.h"
2927
#include "env/task_state.h"
3028

3129
#include "env/command.h"
@@ -54,8 +52,8 @@ class Generator : public internal::BuilderInterface {
5452
parallel_(parallel) {
5553
Initialize();
5654
}
57-
virtual ~Generator() {}
58-
Generator(const Generator &generator) = delete;
55+
virtual ~Generator() = default;
56+
Generator(const Generator &) = delete;
5957

6058
/**
6159
* @brief Add default arguments for input, output and command requirements

buildcc/lib/target/include/target/interface/builder_interface.h

+60
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,66 @@
2727

2828
namespace buildcc::internal {
2929

30+
enum PathState {
31+
kNoChange,
32+
kRemoved,
33+
kAdded,
34+
kUpdated,
35+
};
36+
37+
template <typename T>
38+
inline bool CheckChanged(const T &previous, const T &current) {
39+
bool changed = false;
40+
if (previous != current) {
41+
changed = true;
42+
}
43+
return changed;
44+
}
45+
46+
/**
47+
* @brief
48+
*
49+
* @return PathState Returns first state found if `Removed`, `Added` or
50+
* `Updated`
51+
* If none of the above states are true then it returns `NoChange`
52+
*/
53+
inline PathState CheckPaths(const internal::path_unordered_set &previous_path,
54+
const internal::path_unordered_set &current_path) {
55+
PathState state{PathState::kNoChange};
56+
57+
// * Old path is removed
58+
const bool removed = std::any_of(
59+
previous_path.begin(), previous_path.end(), [&](const internal::Path &p) {
60+
return current_path.find(p) == current_path.end();
61+
});
62+
if (removed) {
63+
state = PathState::kRemoved;
64+
} else {
65+
(void)std::any_of(current_path.cbegin(), current_path.cend(),
66+
[&](const internal::Path &p) -> bool {
67+
bool dirty = false;
68+
const auto find = previous_path.find(p);
69+
const bool added_cond = (find == previous_path.end());
70+
if (added_cond) {
71+
dirty = true;
72+
state = kAdded;
73+
} else {
74+
const bool updated_cond =
75+
(p.GetLastWriteTimestamp() >
76+
find->GetLastWriteTimestamp());
77+
if (updated_cond) {
78+
dirty = true;
79+
state = kUpdated;
80+
} else {
81+
dirty = false;
82+
}
83+
}
84+
return dirty;
85+
});
86+
}
87+
return state;
88+
}
89+
3090
// TODO, 1. Consider updating Recheck* APIs - do not modify internal `dirty_`
3191
// flag
3292
// TODO, 2. Consider removing dependency on target/common/util.h
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "target/custom_generator.h"
2+
3+
#include "expect_custom_generator.h"
4+
5+
#include "CppUTestExt/MockSupport.h"
6+
7+
namespace buildcc {
8+
9+
static constexpr const char *const ID_REMOVED_FUNCTION =
10+
"CustomGenerator::IdRemoved";
11+
static constexpr const char *const ID_ADDED_FUNCTION =
12+
"CustomGenerator::IdAdded";
13+
static constexpr const char *const ID_UPDATED_FUNCTION =
14+
"CustomGenerator::IdUpdated";
15+
16+
void CustomGenerator::IdRemoved() {
17+
mock().actualCall(ID_REMOVED_FUNCTION).onObject(this);
18+
}
19+
void CustomGenerator::IdAdded() {
20+
mock().actualCall(ID_ADDED_FUNCTION).onObject(this);
21+
}
22+
void CustomGenerator::IdUpdated() {
23+
mock().actualCall(ID_UPDATED_FUNCTION).onObject(this);
24+
}
25+
26+
namespace m {
27+
28+
void CustomGeneratorExpect_IdRemoved(unsigned int calls,
29+
CustomGenerator *generator) {
30+
mock().expectNCalls(calls, ID_REMOVED_FUNCTION).onObject(generator);
31+
}
32+
void CustomGeneratorExpect_IdAdded(unsigned int calls,
33+
CustomGenerator *generator) {
34+
mock().expectNCalls(calls, ID_ADDED_FUNCTION).onObject(generator);
35+
}
36+
void CustomGeneratorExpect_IdUpdated(unsigned int calls,
37+
CustomGenerator *generator) {
38+
mock().expectNCalls(calls, ID_UPDATED_FUNCTION).onObject(generator);
39+
}
40+
41+
} // namespace m
42+
43+
} // namespace buildcc

0 commit comments

Comments
 (0)