Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor command execution #986

Merged
merged 30 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b4826ce
add Command class
yaito3014 Sep 13, 2024
709c55e
split into cc
yaito3014 Sep 13, 2024
26d06fc
add missing newline
yaito3014 Sep 13, 2024
5248fbf
emit log
yaito3014 Sep 14, 2024
04c8551
replace system() with execvp()
yaito3014 Sep 14, 2024
75ead28
remove pipe from execute
yaito3014 Sep 14, 2024
b350f2c
mark constructor explicit
yaito3014 Sep 14, 2024
3c86157
remove execute, replace popen
yaito3014 Sep 14, 2024
9fee2b9
rewrite with Command
yaito3014 Sep 16, 2024
f1efeb7
add deps
yaito3014 Sep 16, 2024
9e6e797
IWYU
yaito3014 Sep 16, 2024
ddd6d18
fix poac tidy
yaito3014 Sep 16, 2024
c8e644b
exec family cannot find 'command' command
yaito3014 Sep 16, 2024
905dfca
macOS fix
yaito3014 Sep 16, 2024
86b04ce
fix GCC error
yaito3014 Sep 16, 2024
7dbcb95
use unreachable()
yaito3014 Sep 17, 2024
d349be0
use getenv to retrieve clang-format executable
yaito3014 Sep 17, 2024
b2d0ea8
revert to isystem for deps
yaito3014 Sep 17, 2024
c4a9e4f
implement Command::spawn
yaito3014 Sep 17, 2024
d0bd3a1
use `which` to detect executable
yaito3014 Sep 17, 2024
b5d4fce
set blocking mode
yaito3014 Sep 17, 2024
a0be86e
close pipe after waitpid
yaito3014 Sep 17, 2024
2b2b2be
Revert "set blocking mode"
yaito3014 Sep 18, 2024
f6cae40
chore: IWYU
yaito3014 Sep 18, 2024
aeaea60
no need to fmt::join
yaito3014 Sep 18, 2024
279bb53
verbose type
yaito3014 Sep 18, 2024
b36a1d7
add noexcept
yaito3014 Sep 18, 2024
6b1f937
fix misuse of WEXITSTATUS
yaito3014 Sep 18, 2024
2045c4f
add StdioConfig
yaito3014 Sep 24, 2024
d5e2ce3
child should inherit stdin
yaito3014 Sep 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ $(O)/tests/test_BuildConfig: $(O)/tests/test_BuildConfig.o $(O)/Algos.o \
$(O)/TermColor.o $(O)/Manifest.o $(O)/Parallelism.o $(O)/Semver.o \
$(O)/VersionReq.o $(O)/Git2/Repository.o $(O)/Git2/Object.o $(O)/Git2/Oid.o \
$(O)/Git2/Global.o $(O)/Git2/Config.o $(O)/Git2/Exception.o $(O)/Git2/Time.o \
$(O)/Git2/Commit.o
$(O)/Git2/Commit.o $(O)/Command.o
$(CXX) $(CXXFLAGS) $^ $(LIBS) $(LDFLAGS) -o $@

$(O)/tests/test_Algos: $(O)/tests/test_Algos.o $(O)/TermColor.o
$(O)/tests/test_Algos: $(O)/tests/test_Algos.o $(O)/TermColor.o $(O)/Command.o
$(CXX) $(CXXFLAGS) $^ $(LIBS) $(LDFLAGS) -o $@

$(O)/tests/test_Semver: $(O)/tests/test_Semver.o $(O)/TermColor.o
Expand All @@ -110,7 +110,7 @@ $(O)/tests/test_VersionReq: $(O)/tests/test_VersionReq.o $(O)/TermColor.o \
$(O)/tests/test_Manifest: $(O)/tests/test_Manifest.o $(O)/TermColor.o \
$(O)/Semver.o $(O)/VersionReq.o $(O)/Algos.o $(O)/Git2/Repository.o \
$(O)/Git2/Global.o $(O)/Git2/Oid.o $(O)/Git2/Config.o $(O)/Git2/Exception.o \
$(O)/Git2/Object.o
$(O)/Git2/Object.o $(O)/Command.o
$(CXX) $(CXXFLAGS) $^ $(LIBS) $(LDFLAGS) -o $@


Expand Down
47 changes: 11 additions & 36 deletions src/Algos.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Algos.hpp"

#include "Command.hpp"
#include "Exception.hpp"
#include "Logger.hpp"
#include "Rustify.hpp"
Expand Down Expand Up @@ -39,48 +40,23 @@ toMacroName(const std::string_view name) noexcept {
}

int
execCmd(const std::string_view cmd) noexcept {
execCmd(const Command& cmd) noexcept {
logger::debug("Running `", cmd, '`');
const int status = std::system(cmd.data());
const int exitCode = status >> 8;
return exitCode;
}

static std::pair<std::string, int>
getCmdOutputImpl(const std::string_view cmd) {
constexpr usize bufferSize = 128;
std::array<char, bufferSize> buffer{};
std::string output;

FILE* pipe = popen(cmd.data(), "r");
if (!pipe) {
throw PoacError("popen() failed!");
}

while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
output += buffer.data();
}

const int status = pclose(pipe);
if (status == -1) {
throw PoacError("pclose() failed!");
}
const int exitCode = status >> 8;
return { output, exitCode };
return cmd.output().exitCode;
yaito3014 marked this conversation as resolved.
Show resolved Hide resolved
}

std::string
getCmdOutput(const std::string_view cmd, const usize retry) {
getCmdOutput(const Command& cmd, const usize retry) {
logger::debug("Running `", cmd, '`');

int exitCode = EXIT_SUCCESS;
int waitTime = 1;
for (usize i = 0; i < retry; ++i) {
const auto result = getCmdOutputImpl(cmd);
if (result.second == EXIT_SUCCESS) {
return result.first;
const auto [output, status] = cmd.output();
if (status == EXIT_SUCCESS) {
return output;
}
exitCode = result.second;
exitCode = status;

// Sleep for an exponential backoff.
std::this_thread::sleep_for(std::chrono::seconds(waitTime));
Expand All @@ -91,10 +67,9 @@ getCmdOutput(const std::string_view cmd, const usize retry) {

bool
commandExists(const std::string_view cmd) noexcept {
std::string checkCmd = "command -v ";
checkCmd += cmd;
checkCmd += " >/dev/null 2>&1";
return execCmd(checkCmd) == EXIT_SUCCESS;
std::string checkCmd = "command -v " + std::string(cmd);
int status = std::system(checkCmd.c_str());
yaito3014 marked this conversation as resolved.
Show resolved Hide resolved
return WEXITSTATUS(status) == EXIT_SUCCESS;
}

// ref: https://wandbox.org/permlink/zRjT41alOHdwcf00
Expand Down
5 changes: 3 additions & 2 deletions src/Algos.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "Command.hpp"
#include "Exception.hpp"
#include "Rustify.hpp"

Expand All @@ -20,8 +21,8 @@
std::string toUpper(std::string_view str) noexcept;
std::string toMacroName(std::string_view name) noexcept;

int execCmd(std::string_view cmd) noexcept;
std::string getCmdOutput(std::string_view cmd, usize retry = 3);
int execCmd(const Command& cmd) noexcept;
std::string getCmdOutput(const Command& cmd, usize retry = 3);
bool commandExists(std::string_view cmd) noexcept;

template <typename T>
Expand Down
112 changes: 57 additions & 55 deletions src/BuildConfig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ struct BuildConfig {

std::string OUT_DIR;
std::string CXX = "clang++";
std::string CXXFLAGS;
std::string DEFINES;
std::string INCLUDES = " -I../../include";
std::string LIBS;
std::vector<std::string> CXXFLAGS;
std::vector<std::string> DEFINES;
std::vector<std::string> INCLUDES = { "-I../../include" };
std::vector<std::string> LIBS;

BuildConfig() = default;
explicit BuildConfig(const std::string& packageName)
Expand Down Expand Up @@ -334,21 +334,20 @@ BuildConfig::emitCompdb(const std::string_view baseDir, std::ostream& os)
const std::string file = targetInfo.sourceFile.value();
// The output is the target.
const std::string output = target;
std::string cmd = CXX;
cmd += ' ';
cmd += CXXFLAGS;
cmd += DEFINES;
cmd += INCLUDES;
cmd += " -c ";
cmd += file;
cmd += " -o ";
cmd += output;
const Command cmd = Command(CXX)
.addArgs(CXXFLAGS)
.addArgs(DEFINES)
.addArgs(INCLUDES)
.addArg("-c")
.addArg(file)
.addArg("-o")
.addArg(output);

oss << indent1 << "{\n";
oss << indent2 << "\"directory\": " << baseDirPath << ",\n";
oss << indent2 << "\"file\": " << std::quoted(file) << ",\n";
oss << indent2 << "\"output\": " << std::quoted(output) << ",\n";
oss << indent2 << "\"command\": " << std::quoted(cmd) << "\n";
oss << indent2 << "\"command\": " << std::quoted(cmd.to_string()) << "\n";
oss << indent1 << "},\n";
}

Expand All @@ -366,18 +365,14 @@ BuildConfig::emitCompdb(const std::string_view baseDir, std::ostream& os)

std::string
BuildConfig::runMM(const std::string& sourceFile, const bool isTest) const {
std::string command = "cd ";
command += getOutDir();
command += " && ";
command += CXX;
command += CXXFLAGS;
command += DEFINES;
command += INCLUDES;
Command command =
Command(CXX).addArgs(CXXFLAGS).addArgs(DEFINES).addArgs(INCLUDES);
if (isTest) {
command += " -DPOAC_TEST";
command.addArg("-DPOAC_TEST");
}
command += " -MM ";
command += sourceFile;
command.addArg("-MM");
command.addArg(sourceFile);
command.setWorkingDirectory(getOutDir());
return getCmdOutput(command);
}

Expand Down Expand Up @@ -430,17 +425,16 @@ BuildConfig::containsTestCode(const std::string& sourceFile) const {
while (std::getline(ifs, line)) {
if (line.find("POAC_TEST") != std::string::npos) {
// TODO: Can't we somehow elegantly make the compiler command sharable?
std::string command = CXX;
command += " -E ";
command += CXXFLAGS;
command += DEFINES;
command += INCLUDES;
command += ' ';
command += sourceFile;
Command command(CXX);
command.addArg("-E");
command.addArgs(CXXFLAGS);
command.addArgs(DEFINES);
command.addArgs(INCLUDES);
command.addArg(sourceFile);

const std::string src = getCmdOutput(command);

command += " -DPOAC_TEST";
command.addArg("-DPOAC_TEST");
const std::string testSrc = getCmdOutput(command);

// If the source file contains POAC_TEST, by processing the source
Expand Down Expand Up @@ -570,42 +564,48 @@ void
BuildConfig::installDeps(const bool includeDevDeps) {
const std::vector<DepMetadata> deps = installDependencies(includeDevDeps);
for (const DepMetadata& dep : deps) {
INCLUDES += ' ' + dep.includes;
LIBS += ' ' + dep.libs;
if (!dep.includes.empty())
INCLUDES.push_back(dep.includes);
if (!dep.libs.empty())
LIBS.push_back(dep.libs);
}
logger::debug("INCLUDES: ", INCLUDES);
logger::debug("LIBS: ", LIBS);
logger::debug(fmt::format("INCLUDES: {}", fmt::join(INCLUDES, " ")));
logger::debug(fmt::format("LIBS: {}", fmt::join(LIBS, " ")));
}

void
BuildConfig::addDefine(
const std::string_view name, const std::string_view value
) {
DEFINES += fmt::format(" -D{}='\"{}\"'", name, value);
DEFINES.push_back(fmt::format("-D{}='\"{}\"'", name, value));
}

void
BuildConfig::setVariables(const bool isDebug) {
this->defineCondVar("CXX", CXX);

CXXFLAGS += " -std=c++" + getPackageEdition().getString();
CXXFLAGS.push_back("-std=c++" + getPackageEdition().getString());
if (shouldColor()) {
CXXFLAGS += " -fdiagnostics-color";
CXXFLAGS.push_back("-fdiagnostics-color");
}
if (isDebug) {
CXXFLAGS += " -g -O0 -DDEBUG";
CXXFLAGS.push_back("-g");
CXXFLAGS.push_back("-O0");
CXXFLAGS.push_back("-DDEBUG");
} else {
CXXFLAGS += " -O3 -DNDEBUG";
CXXFLAGS.push_back("-O3");
CXXFLAGS.push_back("-DNDEBUG");
}
const Profile& profile = isDebug ? getDevProfile() : getReleaseProfile();
if (profile.lto) {
CXXFLAGS += " -flto";
CXXFLAGS.push_back("-flto");
}
for (const std::string_view flag : profile.cxxflags) {
CXXFLAGS += ' ';
CXXFLAGS += flag;
CXXFLAGS.emplace_back(flag);
}
this->defineSimpleVar("CXXFLAGS", CXXFLAGS);
this->defineSimpleVar(
"CXXFLAGS", fmt::format("{:s}", fmt::join(CXXFLAGS, " "))
ken-matsui marked this conversation as resolved.
Show resolved Hide resolved
);

const std::string pkgName = toMacroName(this->packageName);
const Version& pkgVersion = getPackageVersion();
Expand Down Expand Up @@ -646,9 +646,13 @@ BuildConfig::setVariables(const bool isDebug) {
addDefine(key, val);
}

this->defineSimpleVar("DEFINES", DEFINES);
this->defineSimpleVar("INCLUDES", INCLUDES);
this->defineSimpleVar("LIBS", LIBS);
this->defineSimpleVar(
"DEFINES", fmt::format("{:s}", fmt::join(DEFINES, " "))
);
this->defineSimpleVar(
"INCLUDES", fmt::format("{:s}", fmt::join(INCLUDES, " "))
);
this->defineSimpleVar("LIBS", fmt::format("{:s}", fmt::join(LIBS, " ")));
}

void
Expand Down Expand Up @@ -956,18 +960,16 @@ modeToProfile(const bool isDebug) {
return isDebug ? "dev" : "release";
}

std::string
Command
getMakeCommand() {
std::string makeCommand;
if (isVerbose()) {
makeCommand = "make";
} else {
makeCommand = "make -s --no-print-directory Q=@";
Command makeCommand("make");
if (!isVerbose()) {
makeCommand.addArg("-s").addArg("--no-print-directory").addArg("Q=@");
}

const usize numThreads = getParallelism();
if (numThreads > 1) {
makeCommand += " -j" + std::to_string(numThreads);
makeCommand.addArg("-j" + std::to_string(numThreads));
}

return makeCommand;
Expand Down
4 changes: 3 additions & 1 deletion src/BuildConfig.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "Command.hpp"

#include <string>
#include <unordered_set>

Expand All @@ -16,4 +18,4 @@ std::string emitMakefile(bool isDebug, bool includeDevDeps);
std::string emitCompdb(bool isDebug, bool includeDevDeps);
std::string_view modeToString(bool isDebug);
std::string_view modeToProfile(bool isDebug);
std::string getMakeCommand();
Command getMakeCommand();
2 changes: 1 addition & 1 deletion src/Cmd/Build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ buildImpl(std::string& outDir, const bool isDebug) {
const auto start = std::chrono::steady_clock::now();

outDir = emitMakefile(isDebug, /*includeDevDeps=*/false);
const std::string makeCommand = getMakeCommand() + " -C " + outDir;
const auto makeCommand = getMakeCommand().addArg("-C").addArg(outDir);
const int exitCode = execCmd(makeCommand);

const auto end = std::chrono::steady_clock::now();
Expand Down
24 changes: 15 additions & 9 deletions src/Cmd/Fmt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

static int fmtMain(std::span<const std::string_view> args);
Expand All @@ -31,7 +32,7 @@ const Subcmd FMT_CMD =
static void
collectFormatTargetFiles(
const fs::path& manifestDir, const std::vector<fs::path>& excludes,
std::string& clangFormatArgs
std::vector<std::string>& clangFormatArgs
) {
// Read git repository if exists
git2::Repository repo = git2::Repository();
Expand Down Expand Up @@ -74,7 +75,7 @@ collectFormatTargetFiles(

const std::string ext = path.extension().string();
if (SOURCE_FILE_EXTS.contains(ext) || HEADER_FILE_EXTS.contains(ext)) {
clangFormatArgs += ' ' + path.string();
clangFormatArgs.push_back(path.string());
}
}
}
Expand Down Expand Up @@ -114,22 +115,27 @@ fmtMain(const std::span<const std::string_view> args) {
}

const std::string_view packageName = getPackageName();
std::string clangFormatArgs = "--style=file --fallback-style=LLVM -Werror";
std::vector<std::string> clangFormatArgs{
"--style=file",
"--fallback-style=LLVM",
"-Werror",
};
if (isVerbose()) {
clangFormatArgs += " --verbose";
clangFormatArgs.push_back("--verbose");
}
if (isCheck) {
clangFormatArgs += " --dry-run";
clangFormatArgs.push_back("--dry-run");
} else {
clangFormatArgs += " -i";
clangFormatArgs.push_back("-i");
logger::info("Formatting", packageName);
}

const fs::path& manifestDir = getManifestPath().parent_path();
collectFormatTargetFiles(manifestDir, excludes, clangFormatArgs);

const std::string clangFormat = "cd " + manifestDir.string()
+ " && ${POAC_FMT:-clang-format} "
+ clangFormatArgs;
const Command clangFormat =
Command("${POAC_FMT:-clang-format}", std::move(clangFormatArgs))
yaito3014 marked this conversation as resolved.
Show resolved Hide resolved
.setWorkingDirectory(manifestDir.string());

return execCmd(clangFormat);
}
Loading
Loading