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

Generalize the dotnet wrapper a bit #131

Merged
merged 1 commit into from
Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 36 additions & 0 deletions csharp/private/actions/wrapper.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""
An action that generates code for the C++ dotnet wrapper.
"""

def write_wrapper_main_cc(ctx, name, template, dotnetexe, argv1 = None, argv2 = None):
"""Create the *.main.cc file which wraps dotnet.exe.

Args:
ctx: Rule context
name: The name of the associated target for this actione
template: The template .cc file
dotnetexe: The File object for dotnet.exe
argv1: (Optional) a value to inject as argv1
argv2: (Optional) a value to inject as argv2

Returns:
A File object to be used in a cc_binary target.
"""

main_cc = ctx.actions.declare_file("%s.main.cc" % name)

# Trim leading "../"
# e.g. ../netcore-sdk-osx/dotnet
dotnetexe_path = dotnetexe.short_path[3:]

ctx.actions.expand_template(
template = template,
output = main_cc,
substitutions = {
"{DotnetExe}": dotnetexe_path,
"{Argv1}": argv1 or "",
"{Argv2}": argv2 or "",
},
)

return main_cc
17 changes: 4 additions & 13 deletions csharp/private/rules/wrapper.bzl
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
"""
A wrapper around `dotnet` for Bazel.
"""
_TEMPLATE = "//csharp/private:wrappers/dotnet.cc"

def _dotnet_wrapper_impl(ctx):
cc_file = ctx.actions.declare_file("%s.cc" % (ctx.attr.name))
if len(ctx.files.src) < 1:
fail("No dotnet executable found in the SDK")
load("//csharp/private:actions/wrapper.bzl", "write_wrapper_main_cc")

ctx.actions.expand_template(
template = ctx.file.template,
output = cc_file,
substitutions = {
"{DotnetExe}": ctx.files.src[0].short_path[3:],
},
)
def _dotnet_wrapper_impl(ctx):
cc_file = write_wrapper_main_cc(ctx, ctx.attr.name, ctx.file.template, ctx.files.src[0])

files = depset(direct = [cc_file])
return [
Expand All @@ -29,7 +20,7 @@ dotnet_wrapper = rule(
"template": attr.label(
doc = """Path to the program that will wrap the dotnet executable.
This program will be compiled and used instead of directly calling the dotnet executable.""",
default = Label(_TEMPLATE),
default = Label("//csharp/private:wrappers/dotnet.cc"),
allow_single_file = True,
),
"src": attr.label_list(
Expand Down
49 changes: 41 additions & 8 deletions csharp/private/wrappers/dotnet.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
Expand All @@ -21,17 +22,29 @@ std::string evprintf(std::string name, std::string path) {
return ss.str();
}

// Bazel template strings. Optional args may == "".
// kDotnetExe: path to dotnet.exe
// kArgv1: (optional) first arg to dotnet.exe
// kArgv2: (optional) second arg to dotnet.exe
constexpr const char* kDotnetExe = "{DotnetExe}";
constexpr const char* kArgv1 = "{Argv1}";
constexpr const char* kArgv2 = "{Argv2}";

static_assert(
!(kArgv1[0] == '\0' && kArgv2[0] != '\0'),
"kArgv2 can only be specified if kArgv1 is too"
);

int main(int argc, char** argv) {
std::string error;

auto runfiles = Runfiles::Create(argv[0], &error);

if (runfiles == nullptr) {
std::cerr << "Couldn't load runfiles: " << error << std::endl;
return 101;
}

auto dotnet = runfiles->Rlocation("{DotnetExe}");
auto dotnet = runfiles->Rlocation(kDotnetExe);
if (dotnet.empty()) {
std::cerr << "Couldn't find the .NET runtime" << std::endl;
return 404;
Expand All @@ -50,14 +63,34 @@ int main(int argc, char** argv) {
evprintf("DOTNET_CLI_TELEMETRY_OPTOUT", "1"), // disable telemetry
};

auto extra_count =
!!(std::strlen(kArgv1) != 0) +
!!(std::strlen(kArgv2) != 0);

auto dotnet_argv = new char*[extra_count + argc + 1];

// dotnet wants this to either be dotnet or dotnet.exe but doesn't have a
// preference otherwise.
auto dotnet_argv = new char*[argc + 1];
dotnet_argv[0] = (char*)"dotnet";
dotnet_argv[0] = const_cast<char*>("dotnet");

// Make sure to hold a reference to these std::string.
j3parker marked this conversation as resolved.
Show resolved Hide resolved
auto argv1 = runfiles->Rlocation(kArgv1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto argv2 = runfiles->Rlocation(kArgv2);

if (std::strlen(kArgv1) != 0) {
dotnet_argv[1] = const_cast<char*>(argv1.c_str());
}

if (std::strlen(kArgv2) != 0) {
dotnet_argv[2] = const_cast<char*>(argv2.c_str());
}

// Copy the rest of our arguments in
for (int i = 1; i < argc; i++) {
dotnet_argv[i] = argv[i];
dotnet_argv[extra_count + i] = argv[i];
}
dotnet_argv[argc] = nullptr;

dotnet_argv[extra_count + argc] = nullptr;

#ifdef _WIN32
// _spawnve has a limit on the size of the environment variables
Expand All @@ -83,9 +116,9 @@ int main(int argc, char** argv) {
auto result = execve(dotnet.c_str(), const_cast<char**>(dotnet_argv), envp);
#endif // _WIN32
if (result != 0) {
std::cout << "dotnet failed: " << errno << std::endl;
perror("dotnet failed");
return -1;
}

return result;
}
}