| # Copyright 2015 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """CSharp bazel rules""" |
| |
| load("//tools/build_rules:deprecation.bzl", "deprecated") |
| |
| def warning(rule): |
| print(deprecated( |
| "dotnet", |
| rule, |
| "@bazel_tools//tools/build_defs/dotnet:csharp.bzl", |
| "@io_bazel_rules_dotnet//dotnet:csharp.bzl")) |
| |
| _MONO_UNIX_CSC = "/usr/local/bin/mcs" |
| |
| # TODO(jeremy): Windows when it's available. |
| |
| def _make_csc_flag(flag_start, flag_name, flag_value=None): |
| return flag_start + flag_name + (":" + flag_value if flag_value else "") |
| |
| def _make_csc_deps(deps, extra_files=[]): |
| dlls = set() |
| refs = set() |
| transitive_dlls = set() |
| for dep in deps: |
| if hasattr(dep, "target_type"): |
| dep_type = getattr(dep, "target_type") |
| if dep_type == "exe": |
| fail("You can't use a binary target as a dependency") |
| if dep_type == "library": |
| dlls += [dep.out] |
| refs += [dep.name] |
| if dep.transitive_dlls: |
| transitive_dlls += dep.transitive_dlls |
| return struct( |
| dlls = dlls + set(extra_files), |
| refs = refs, |
| transitive_dlls = transitive_dlls) |
| |
| def _get_libdirs(dlls, libdirs=[]): |
| return [dep.dirname for dep in dlls] + libdirs |
| |
| def _make_csc_arglist(ctx, output, depinfo, extra_refs=[]): |
| flag_start = ctx.attr._flag_start |
| args = [ |
| # /out:<file> |
| _make_csc_flag(flag_start, "out", output.path), |
| # /target (exe for binary, library for lib, module for module) |
| _make_csc_flag(flag_start, "target", ctx.attr._target_type), |
| # /fullpaths |
| _make_csc_flag(flag_start, "fullpaths"), |
| # /warn |
| _make_csc_flag(flag_start, "warn", str(ctx.attr.warn)), |
| # /nologo |
| _make_csc_flag(flag_start, "nologo"), |
| ] |
| |
| # /modulename:<string> only used for modules |
| libdirs = _get_libdirs(depinfo.dlls) |
| libdirs = _get_libdirs(depinfo.transitive_dlls, libdirs) |
| # /lib:dir1,[dir1] |
| args += [_make_csc_flag(flag_start, "lib", ",".join(list(libdirs)))] if libdirs else [] |
| # /reference:filename[,filename2] |
| args += [_make_csc_flag(flag_start, "reference", ",".join(list(depinfo.refs + extra_refs)))] if depinfo.refs else extra_refs |
| |
| # /doc |
| args += [_make_csc_flag(flag_start, "doc", ctx.outputs.doc_xml.path)] if hasattr(ctx.outputs, "doc_xml") else [] |
| # /debug |
| debug = ctx.var.get("BINMODE", "") == "-dbg" |
| args += [_make_csc_flag(flag_start, "debug")] if debug else [] |
| # /warnaserror |
| # TODO(jeremy): /define:name[;name2] |
| # TODO(jeremy): /resource:filename[,identifier[,accesibility-modifier]] |
| # /main:class |
| if hasattr(ctx.attr, "main_class") and ctx.attr.main_class: |
| args += [_make_csc_flag(flag_start, "main", ctx.attr.main_class)] |
| # TODO(jwall): /parallel |
| return args |
| |
| def _make_nunit_launcher(ctx, depinfo, output): |
| content = """#!/bin/bash |
| cd $0.runfiles |
| # TODO(jeremy): This is a gross and fragile hack. |
| # We should be able to do better than this. |
| for l in {libs}; do |
| ln -s -f $l $(basename $l) |
| done |
| /usr/local/bin/mono {nunit_exe} {libs} "$@" |
| |
| """ |
| libs = [d.short_path for d in depinfo.dlls] |
| libs += [d.short_path for d in depinfo.transitive_dlls] |
| |
| content = content.format( |
| nunit_exe=ctx.files._nunit_exe[0].path, |
| libs=" ".join(libs)) |
| |
| ctx.file_action( |
| output=ctx.outputs.executable, |
| content=content) |
| |
| def _make_launcher(ctx, depinfo, output): |
| content = """#!/bin/bash |
| cd $0.runfiles |
| # TODO(jeremy): This is a gross and fragile hack. |
| # We should be able to do better than this. |
| ln -s -f {exe} $(basename {exe}) |
| for l in {libs}; do |
| ln -s -f $l $(basename $l) |
| done |
| /usr/local/bin/mono $(basename {exe}) "$@" |
| """ |
| libs = [d.short_path for d in depinfo.dlls] |
| libs += [d.short_path for d in depinfo.transitive_dlls] |
| |
| content = content.format( |
| exe=output.short_path, |
| libs=" ".join(libs)) |
| |
| ctx.file_action( |
| output=ctx.outputs.executable, |
| content=content) |
| |
| def _csc_get_output(ctx): |
| output = None |
| if hasattr(ctx.outputs, "csc_lib"): |
| output = ctx.outputs.csc_lib |
| elif hasattr(ctx.outputs, "csc_exe"): |
| output = ctx.outputs.csc_exe |
| else: |
| fail("You must supply one of csc_lib or csc_exe") |
| return output |
| |
| def _csc_collect_inputs(ctx, extra_files=[]): |
| depinfo = _make_csc_deps(ctx.attr.deps, extra_files=extra_files) |
| inputs = set(ctx.files.srcs) + depinfo.dlls + depinfo.transitive_dlls |
| srcs = [src.path for src in ctx.files.srcs] |
| return struct( |
| depinfo=depinfo, |
| inputs=inputs, |
| srcs=srcs) |
| |
| def _csc_compile_action(ctx, assembly, all_outputs, collected_inputs, extra_refs=[]): |
| csc_args = _make_csc_arglist(ctx, assembly, collected_inputs.depinfo, extra_refs=extra_refs) |
| command_script = " ".join([ctx.attr.csc] + csc_args + collected_inputs.srcs) |
| |
| ctx.action( |
| inputs = list(collected_inputs.inputs), |
| outputs = all_outputs, |
| command = command_script, |
| arguments = csc_args, |
| progress_message = ("Compiling " + |
| ctx.label.package + ":" + |
| ctx.label.name)) |
| |
| def _cs_runfiles(ctx, outputs, depinfo): |
| return ctx.runfiles( |
| files = outputs, |
| transitive_files = set(depinfo.dlls + depinfo.transitive_dlls) or None) |
| |
| def _csc_compile_impl(ctx): |
| warning("csharp_library") |
| if hasattr(ctx.outputs, "csc_lib") and hasattr(ctx.outputs, "csc_exe"): |
| fail("exactly one of csc_lib and csc_exe must be defined") |
| |
| output = _csc_get_output(ctx) |
| outputs = [output] + ([ctx.outputs.doc_xml] if hasattr(ctx.outputs, "doc_xml") else []) |
| |
| collected = _csc_collect_inputs(ctx) |
| |
| depinfo = collected.depinfo |
| inputs = collected.inputs |
| srcs = collected.srcs |
| |
| runfiles = _cs_runfiles(ctx, outputs, depinfo) |
| |
| _csc_compile_action(ctx, output, outputs, collected) |
| |
| if hasattr(ctx.outputs, "csc_exe"): |
| _make_launcher(ctx, depinfo, output) |
| |
| return struct(name= ctx.label.name, |
| srcs = srcs, |
| target_type=ctx.attr._target_type, |
| out = output, |
| dlls = set([output]), |
| transitive_dlls = depinfo.dlls, |
| runfiles=runfiles) |
| |
| def _cs_nunit_run_impl(ctx): |
| warning("csharp_nunit_test") |
| if hasattr(ctx.outputs, "csc_lib") and hasattr(ctx.outputs, "csc_exe"): |
| fail("exactly one of csc_lib and csc_exe must be defined") |
| |
| output = _csc_get_output(ctx) |
| outputs = [output] + ([ctx.outputs.doc_xml] if hasattr(ctx.outputs, "doc_xml") else []) |
| outputs = outputs |
| |
| collected_inputs = _csc_collect_inputs(ctx, ctx.files._nunit_framework) |
| |
| depinfo = collected_inputs.depinfo |
| inputs = collected_inputs.inputs |
| srcs = collected_inputs.srcs |
| |
| runfiles = _cs_runfiles(ctx, outputs + ctx.files._nunit_exe + ctx.files._nunit_exe_libs, depinfo) |
| |
| _csc_compile_action(ctx, output, outputs, collected_inputs, extra_refs=["Nunit.Framework"]) |
| |
| _make_nunit_launcher(ctx, depinfo, output) |
| |
| return struct(name=ctx.label.name, |
| srcs=srcs, |
| target_type=ctx.attr._target_type, |
| out=output, |
| dlls = set([output]) if hasattr(ctx.outputs, "csc_lib") else None, |
| transitive_dlls = depinfo.dlls, |
| runfiles=runfiles) |
| |
| _COMMON_ATTRS = { |
| # configuration fragment that specifies |
| "_flag_start": attr.string(default = "-"), |
| # where the csharp compiler is. |
| "csc": attr.string(default = _MONO_UNIX_CSC), |
| # code dependencies for this rule. |
| # all dependencies must provide an out field. |
| "deps": attr.label_list(providers = [ |
| "out", |
| "target_type", |
| ]), |
| # source files for this target. |
| "srcs": attr.label_list(allow_files = FileType([ |
| ".cs", |
| ".resx", |
| ])), |
| # resources to use as dependencies. |
| # TODO(jeremy): "resources_deps": attr.label_list(allow_files=True), |
| #TODO(jeremy): # name of the module if you are creating a module. |
| #TODO(jeremy): "modulename": attri.string(), |
| # warn level to use |
| "warn": attr.int(default = 4), |
| # define preprocessor symbols. |
| #TODO(jeremy): "define": attr.string_list(), |
| } |
| |
| _LIB_ATTRS = {"_target_type": attr.string(default = "library")} |
| |
| _EXE_ATTRS = { |
| "_target_type": attr.string(default = "exe"), |
| # main class to use as entry point. |
| "main_class": attr.string(), |
| } |
| |
| _NUNIT_ATTRS = { |
| "_nunit_exe": attr.label( |
| default = Label("@nunit//:nunit_exe"), |
| single_file = True, |
| ), |
| "_nunit_framework": attr.label(default = Label("@nunit//:nunit_framework")), |
| "_nunit_exe_libs": attr.label(default = Label("@nunit//:nunit_exe_libs")), |
| } |
| |
| _LIB_OUTPUTS = { |
| "csc_lib": "%{name}.dll", |
| "doc_xml": "%{name}.xml", |
| } |
| |
| _BIN_OUTPUTS = { |
| "csc_exe": "%{name}.exe", |
| } |
| |
| csharp_library = rule( |
| attrs = _COMMON_ATTRS + _LIB_ATTRS, |
| outputs = _LIB_OUTPUTS, |
| implementation = _csc_compile_impl, |
| ) |
| |
| csharp_binary = rule( |
| attrs = _COMMON_ATTRS + _EXE_ATTRS, |
| executable = True, |
| outputs = _BIN_OUTPUTS, |
| implementation = _csc_compile_impl, |
| ) |
| |
| csharp_nunit_test = rule( |
| attrs = _COMMON_ATTRS + _LIB_ATTRS + _NUNIT_ATTRS, |
| executable = True, |
| outputs = _LIB_OUTPUTS, |
| test = True, |
| implementation = _cs_nunit_run_impl, |
| ) |
| |
| NUNIT_BUILD_FILE = """ |
| filegroup( |
| name = "nunit_exe", |
| srcs = ["NUnit-2.6.4/bin/nunit-console.exe"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| filegroup( |
| name = "nunit_exe_libs", |
| srcs = glob(["NUnit-2.6.4/bin/lib/*.dll"]), |
| visibility = ["//visibility:public"], |
| ) |
| |
| filegroup( |
| name = "nunit_framework", |
| srcs = glob(["NUnit-2.6.4/bin/framework/*.dll"]), |
| visibility = ["//visibility:public"], |
| ) |
| """ |
| |
| def csharp_repositories(): |
| warning("csharp_repositories") |
| native.new_http_archive( |
| name = "nunit", |
| build_file_content = NUNIT_BUILD_FILE, |
| sha256 = "1bd925514f31e7729ccde40a38a512c2accd86895f93465f3dfe6d0b593d7170", |
| type = "zip", |
| url = "https://github.com/nunit/nunitv2/releases/download/2.6.4/NUnit-2.6.4.zip", |
| ) |