Revision b83ead4201a2f7a0abb6567093e7f18a8ed10f7e authored by Steven Johnson on 05 September 2018, 23:28:29 UTC, committed by Steven Johnson on 05 September 2018, 23:28:29 UTC
1 parent d1becce
Raw File
halide.bzl
load("@halide//:halide_config.bzl", "halide_system_libs")

def halide_language_copts():
  _common_opts = [
      "-DGOOGLE_PROTOBUF_NO_RTTI",
      "-fPIC",
      "-fno-rtti",
      "-std=c++11",
      "-Wno-conversion",
      "-Wno-sign-compare",
  ]
  _posix_opts = [
      "$(STACK_FRAME_UNLIMITED)",
      "-fno-exceptions",
      "-funwind-tables",
      "-fvisibility-inlines-hidden",
  ]
  _msvc_opts = [
      "-D_CRT_SECURE_NO_WARNINGS",
      # Linking with LLVM on Windows requires multithread+DLL CRT
      "/MD",
  ]
  return _common_opts + select({
      "@halide//:halide_platform_config_x64_windows_msvc":
          _msvc_opts,
      "@halide//:halide_platform_config_x64_windows":
          ["/error_please_set_cpu_and_host_cpu_x64_windows_msvc"],
      "@halide//:halide_platform_config_darwin":
          _posix_opts,
      "@halide//:halide_platform_config_darwin_x86_64":
          _posix_opts,
      "//conditions:default":
          _posix_opts,
  })

def halide_language_linkopts():
  _linux_opts = [
      "-rdynamic",
      "-ldl",
      "-lpthread",
      "-lz"
  ]
  _osx_opts = [
      "-Wl,-stack_size",
      "-Wl,1000000"
  ]
  _msvc_opts = []
  return select({
      "@halide//:halide_platform_config_x64_windows_msvc":
          _msvc_opts,
      "@halide//:halide_platform_config_x64_windows":
          ["/error_please_set_cpu_and_host_cpu_x64_windows_msvc"],
      "@halide//:halide_platform_config_darwin":
          _osx_opts,
      "@halide//:halide_platform_config_darwin_x86_64":
          _osx_opts,
      "//conditions:default":
          _linux_opts,
  }) + halide_system_libs().split(" ")


def halide_runtime_linkopts():
  _posix_opts = [
      "-ldl",
      "-lpthread",
  ]
  _android_opts = [
      "-llog",
      "-landroid",
  ]
  _msvc_opts = []
  return select({
      "@halide//:halide_config_arm_32_android":
          _android_opts,
      "@halide//:halide_config_arm_64_android":
          _android_opts,
      "@halide//:halide_config_x86_32_android":
          _android_opts,
      "@halide//:halide_config_x86_64_android":
          _android_opts,
      "@halide//:halide_config_x86_64_windows":
          _msvc_opts,
      "//conditions:default":
          _posix_opts,
  })


def halide_opengl_linkopts():
  _linux_opts = ["-lGL", "-lX11"]
  _osx_opts = ["-framework OpenGL"]
  _msvc_opts = []
  return select({
      "@halide//:halide_config_x86_64_windows":
          _msvc_opts,
      "@halide//:halide_config_x86_32_osx":
          _osx_opts,
      "@halide//:halide_config_x86_64_osx":
          _osx_opts,
      "//conditions:default":
          _linux_opts,
  })


# (halide-target-base, cpus, android-cpu, ios-cpu)
_HALIDE_TARGET_CONFIG_INFO = [
    # Android
    ("arm-32-android", None, "armeabi-v7a", None),
    ("arm-64-android", None, "arm64-v8a", None),
    ("x86-32-android", None, "x86", None),
    ("x86-64-android", None, "x86_64", None),
    # iOS
    ("arm-32-ios", None, None, "armv7"),
    ("arm-64-ios", None, None, "arm64"),
    # OSX
    ("x86-32-osx", None, None, "i386"),
    ("x86-64-osx", None, None, "x86_64"),
    # Linux
    ("arm-64-linux", ["arm"], None, None),
    ("powerpc-64-linux", ["ppc"], None, None),
    ("x86-64-linux", ["k8"], None, None),
    ("x86-32-linux", ["piii"], None, None),
    # Windows
    ("x86-64-windows", ["x64_windows_msvc"], None, None),
    # Special case: Android-ARMEABI. Note that we are using an illegal Target
    # string for Halide; this is intentional. It allows us to add another
    # config_setting to match the armeabi-without-v7a required for certain build
    # scenarios; we special-case this in _select_multitarget to translate it
    # back into a legal Halide target.
    #
    # Note that this won't produce a build that is useful (it will SIGILL on
    # non-v7a hardware), but isn't intended to be useful for anything other
    # than allowing certain builds to complete.
    ("armeabi-32-android", ["armeabi"], "armeabi", None),
]

_HALIDE_TARGET_MAP_DEFAULT = {
    "x86-64-osx": [
        "x86-64-osx-sse41-avx-avx2-fma",
        "x86-64-osx-sse41-avx",
        "x86-64-osx-sse41",
        "x86-64-osx",
    ],
    "x86-64-windows": [
        "x86-64-windows-sse41-avx-avx2-fma",
        "x86-64-windows-sse41-avx",
        "x86-64-windows-sse41",
        "x86-64-windows",
    ],
    "x86-64-linux": [
        "x86-64-linux-sse41-avx-avx2-fma",
        "x86-64-linux-sse41-avx",
        "x86-64-linux-sse41",
        "x86-64-linux",
    ],
    "x86-32-linux": [
        "x86-32-linux-sse41",
        "x86-32-linux",
    ],
}


def halide_library_default_target_map():
  return _HALIDE_TARGET_MAP_DEFAULT


_HALIDE_RUNTIME_OVERRIDES = {
    # Empty placeholder for now; we may add target-specific
    # overrides here in the future.
}


def halide_config_settings():
  """Define config_settings for halide_library.

       These settings are used to distinguish build targets for
       halide_library() based on target CPU and configs. This is provided
       to allow algorithmic generation of the config_settings based on
       internal data structures; it should not be used outside of Halide.

  """
  cpus = [
      "darwin",
      "darwin_x86_64",
      "x64_windows_msvc",
      "x64_windows",
  ]
  for cpu in cpus:
    native.config_setting(
        name="halide_platform_config_%s" % cpu,
        values={
            "cpu": cpu,
        },
        visibility=["//visibility:public"])
  for base_target, _, android_cpu, ios_cpu in _HALIDE_TARGET_CONFIG_INFO:
    if android_cpu == None:
      # "armeabi" is the default value for --android_cpu and isn't considered legal
      # here, so we use the value to assume we aren't building for Android.
      android_cpu = "armeabi"
    if ios_cpu == None:
      # The default value for --ios_cpu is "x86_64", i.e. for the 64b OS X simulator.
      # Assuming that the i386 version of the simulator will be used along side
      # arm32 apps, we consider this value to mean the flag was unspecified; this
      # won't work for 32 bit simulator builds for A6 or older phones.
      ios_cpu = "x86_64"
    for n, cpu in _config_setting_names_and_cpus(base_target):
      if cpu != None:
        values = {
            "cpu": cpu,
            "android_cpu": android_cpu,
            "ios_cpu": ios_cpu,
        }
      else:
        values = {
            "android_cpu": android_cpu,
            "ios_cpu": ios_cpu,
        }
      native.config_setting(
          name=n,
          values=values,
          visibility=["//visibility:public"])

  # Config settings for Sanitizers
  native.config_setting(
      name="halide_config_asan",
      values={"compiler": "asan"},
      visibility=["//visibility:public"])

  native.config_setting(
      name="halide_config_msan",
      values={"compiler": "msan"},
      visibility=["//visibility:public"])

  native.config_setting(
      name="halide_config_tsan",
      values={"compiler": "tsan"},
      visibility=["//visibility:public"])


# Alphabetizes the features part of the target to make sure they always match no
# matter the concatenation order of the target string pieces.
def _canonicalize_target(halide_target):
  if halide_target == "host":
    return halide_target
  if "," in halide_target:
    fail("Multitarget may not be specified here")
  tokens = halide_target.split("-")
  if len(tokens) < 3:
    fail("Illegal target: %s" % halide_target)
  # rejoin the tokens with the features sorted
  return "-".join(tokens[0:3] + sorted(tokens[3:]))


# Converts comma and dash separators to underscore and alphabetizes
# the features part of the target to make sure they always match no
# matter the concatenation order of the target string pieces.
def _halide_target_to_bazel_rule_name(multitarget):
  subtargets = multitarget.split(",")
  subtargets = [_canonicalize_target(st).replace("-", "_") for st in subtargets]
  return "_".join(subtargets)


def _extract_base_target_pieces(halide_target):
  if "," in halide_target:
    fail("Multitarget may not be specified here: %s" % halide_target)
  tokens = halide_target.split("-")
  if len(tokens) != 3:
    fail("Unexpected halide_target form: %s" % halide_target)
  halide_arch = tokens[0]
  halide_bits = tokens[1]
  halide_os = tokens[2]
  return (halide_arch, halide_bits, halide_os)

def _blaze_cpus_for_target(halide_target):
  halide_arch, halide_bits, halide_os = _extract_base_target_pieces(halide_target)
  key = "%s-%s-%s" % (halide_arch, halide_bits, halide_os)
  info = None
  for i in _HALIDE_TARGET_CONFIG_INFO:
    if i[0] == key:
      info = i
      break
  if info == None:
    fail("The target %s is not one we understand (yet)" % key)
  return info[1]

def _config_setting_names_and_cpus(halide_target):
  """Take a Halide target string and converts to a unique name suitable for a Bazel config_setting."""
  halide_arch, halide_bits, halide_os = _extract_base_target_pieces(halide_target)
  cpus = _blaze_cpus_for_target(halide_target)
  if cpus == None:
    cpus = [ None ]
  if len(cpus) > 1:
    return [("halide_config_%s_%s_%s_%s" % (halide_arch, halide_bits, halide_os, cpu), cpu) for cpu in cpus]
  else:
    return [("halide_config_%s_%s_%s" % (halide_arch, halide_bits, halide_os), cpu) for cpu in cpus]



def _config_settings(halide_target):
  return ["@halide//:%s" % s for (s, _) in _config_setting_names_and_cpus(halide_target)]


# The second argument is True if there is a separate file generated
# for each subtarget of a multitarget output, False if not.
_output_extensions = {
    "static_library": ("a", False),
    "o": ("o", False),
    "h": ("h", False),
    "cpp_stub": ("stub.h", False),
    "assembly": ("s.txt", True),
    "bitcode": ("bc", True),
    "stmt": ("stmt", True),
    "schedule": ("schedule", True),
    "html": ("html", True),
    "cpp": ("generated.cpp", True),
}


def _gengen_outputs(filename, halide_target, outputs):
  new_outputs = {}
  for o in outputs:
    if o not in _output_extensions:
      fail("Unknown output: " + o)
    ext, is_multiple = _output_extensions[o]
    if is_multiple and len(halide_target) > 1:
      # Special handling needed for ".s.txt" and similar: the suffix from the
      # is_multiple case always goes before the final .
      # (i.e. "filename.s_suffix.txt", not "filename_suffix.s.txt")
      # -- this is awkward, but is what Halide does, so we must match it.
      pieces = ext.rsplit(".", 1)
      extra = (".%s" % pieces[0]) if len(pieces) > 1 else ""
      ext = pieces[-1]
      for h in halide_target:
        new_outputs[o + h] = "%s%s_%s.%s" % (
            filename, extra, _canonicalize_target(h).replace("-", "_"), ext)
    else:
      new_outputs[o] = "%s.%s" % (filename, ext)
  return new_outputs


def _gengen_impl(ctx):
  if _has_dupes(ctx.attr.outputs):
    fail("Duplicate values in outputs: " + str(ctx.attr.outputs))

  if not ctx.attr.generator_closure.generator_name:
    fail("generator_name must be specified")

  remaps = [".s=.s.txt,.cpp=.generated.cpp"]
  halide_target = ctx.attr.halide_target
  if "windows" in halide_target[-1] and not "mingw" in halide_target[-1]:
    remaps += [".obj=.o", ".lib=.a"]
  if ctx.attr.sanitizer:
    halide_target = []
    for t in ctx.attr.halide_target:
      ct = _canonicalize_target("%s-%s" % (t, ctx.attr.sanitizer))
      halide_target += [ct]
      remaps += ["%s=%s" % (ct.replace("-", "_"), t.replace("-", "_"))]

  outputs = [
      ctx.actions.declare_file(f)
      for f in _gengen_outputs(
          ctx.attr.filename,
          ctx.attr.halide_target,  # *not* halide_target
          ctx.attr.outputs).values()
  ]

  leafname = ctx.attr.filename.split('/')[-1]
  arguments = ["-o", outputs[0].dirname]
  if ctx.attr.generate_runtime:
    arguments += ["-r", leafname]
    if len(halide_target) > 1:
      fail("Only one halide_target allowed here")
    if ctx.attr.halide_function_name:
      fail("halide_function_name not allowed here")
  else:
    arguments += ["-g", ctx.attr.generator_closure.generator_name]
    arguments += ["-n", leafname]
    if ctx.attr.halide_function_name:
      arguments += ["-f", ctx.attr.halide_function_name]

  if ctx.attr.outputs:
    arguments += ["-e", ",".join(ctx.attr.outputs)]
    arguments += ["-x", ",".join(remaps)]
  arguments += ["target=%s" % ",".join(halide_target)]
  if ctx.attr.halide_generator_args:
    arguments += ctx.attr.halide_generator_args.split(" ")

  if ctx.executable.hexagon_code_signer:
    additional_inputs, _, input_manifests = ctx.resolve_command(
        tools=[ctx.attr.hexagon_code_signer])
    hexagon_code_signer = ctx.executable.hexagon_code_signer.path
  else:
    additional_inputs = []
    input_manifests = None
    hexagon_code_signer = ""

  progress_message = "Executing generator %s with target (%s) args (%s)." % (
      ctx.attr.generator_closure.generator_name,
       ",".join(halide_target),
       ctx.attr.halide_generator_args)
  for o in outputs:
    s = o.path
    if s.endswith(".h") or s.endswith(".a") or s.endswith(".lib"):
      continue
    progress_message += "\nEmitting extra Halide output: %s" % s

  env = {
      "HL_DEBUG_CODEGEN": str(ctx.attr.debug_codegen_level),
      "HL_HEXAGON_CODE_SIGNER": hexagon_code_signer,
  }
  ctx.actions.run(
      # If you need to force the tools to run locally (e.g. for experimentation),
      # uncomment this line.
      # execution_requirements={"local":"1"},
      arguments=arguments,
      env=env,
      executable=ctx.attr.generator_closure.generator_binary.files_to_run.executable,
      mnemonic="ExecuteHalideGenerator",
      input_manifests=input_manifests,
      inputs=additional_inputs,
      outputs=outputs,
      progress_message=progress_message
  )


_gengen = rule(
    implementation=_gengen_impl,
    attrs={
        "debug_codegen_level":
            attr.int(),
        "filename":
            attr.string(),
        "generate_runtime":
            attr.bool(default=False),
        "generator_closure":
            attr.label(
                cfg="host", providers=["generator_binary", "generator_name"]),
        "halide_target":
            attr.string_list(),
        "halide_function_name":
            attr.string(),
        "halide_generator_args":
            attr.string(),
        "hexagon_code_signer":
            attr.label(
                executable=True, cfg="host"),
        "outputs":
            attr.string_list(),
        "sanitizer":
            attr.string(),
    },
    outputs=_gengen_outputs,
    output_to_genfiles=True)


def _add_target_features(target, features):
  if "," in target:
    fail("Cannot use multitarget here")
  new_target = target.split("-")
  for f in features:
    if f and f not in new_target:
      new_target += [f]
  return "-".join(new_target)


def _has_dupes(some_list):
  clean = depset(some_list).to_list()
  return sorted(some_list) != sorted(clean)


def _select_multitarget(base_target,
                        halide_target_features,
                        halide_target_map):
  if base_target == "armeabi-32-android":
    base_target = "arm-32-android"
  wildcard_target = halide_target_map.get("*")
  if wildcard_target:
    expected_base = "*"
    targets = wildcard_target
  else:
    expected_base = base_target
    targets = halide_target_map.get(base_target, [base_target])

  multitarget = []
  for t in targets:
    if not t.startswith(expected_base):
      fail(
          "target %s does not start with expected target %s for halide_target_map"
          % (t, expected_base))
    t = t[len(expected_base):]
    if t.startswith("-"):
      t = t[1:]
    # Check for a "match all base targets" entry:
    multitarget.append(_add_target_features(base_target, t.split("-")))

  # Add the extra features (if any).
  if halide_target_features:
    multitarget = [
        _add_target_features(t, halide_target_features) for t in multitarget
    ]

  # Finally, canonicalize all targets
  multitarget = [_canonicalize_target(t) for t in multitarget]
  return multitarget


def _gengen_closure_impl(ctx):
  return struct(
      generator_binary=ctx.attr.generator_binary,
      generator_name=ctx.attr.halide_generator_name)


_gengen_closure = rule(
    implementation=_gengen_closure_impl,
    attrs={
        "generator_binary":
            attr.label(
                executable=True, allow_files=True, mandatory=True, cfg="host"),
        "halide_generator_name":
            attr.string(),
    })

def _discard_useless_features(halide_target_features = []):
  # Discard target features which do not affect the contents of the runtime.
  useless_features = depset(["user_context", "no_asserts", "no_bounds_query", "profile"])
  return sorted(depset([f for f in halide_target_features if f not in useless_features.to_list()]).to_list())

def _halide_library_runtime_target_name(halide_target_features = []):
  return "_".join(["halide_library_runtime"] + _discard_useless_features(halide_target_features))

def _define_halide_library_runtime(halide_target_features = []):
  target_name = _halide_library_runtime_target_name(halide_target_features)

  if not native.existing_rule("halide_library_runtime.generator"):
    halide_generator(
        name="halide_library_runtime.generator",
        srcs=[],
        deps=[],
        visibility=["//visibility:private"])
  condition_deps = {}
  for base_target, _, _, _ in _HALIDE_TARGET_CONFIG_INFO:
    settings = _config_settings(base_target)
    # For armeabi-32-android, just generate an arm-32-android runtime
    halide_target = "arm-32-android" if base_target == "armeabi-32-android" else base_target
    halide_target_name = _halide_target_to_bazel_rule_name(base_target);
    _gengen(
        name="%s_%s" % (halide_target_name, target_name),
        filename="%s/%s" % (halide_target_name, target_name),
        generate_runtime=True,
        generator_closure="halide_library_runtime.generator_closure",
        halide_target=["-".join([halide_target] + _discard_useless_features(halide_target_features))],
        sanitizer=select({
            "@halide//:halide_config_asan": "asan",
            "@halide//:halide_config_msan": "msan",
            "@halide//:halide_config_tsan": "tsan",
            "//conditions:default": "",
        }),
        outputs=["o"],
        tags=["manual"],
        visibility=[
            "@halide//:__subpackages__",
        ]
    )
    for s in settings:
      condition_deps[s] = ["%s/%s.o" % (halide_target_name, target_name)]

  native.cc_library(
      name=target_name,
      linkopts=halide_runtime_linkopts(),
      srcs=select(condition_deps),
      tags=["manual"],
      visibility=["//visibility:public"])

  return target_name

def _standard_library_runtime_features():
  standard_features = [
      [],
      ["asan"],
      ["c_plus_plus_name_mangling"],
      ["cuda"],
      ["cuda", "matlab"],
      ["hvx_64"],
      ["hvx_128"],
      ["matlab"],
      ["metal"],
      ["opengl"],
  ]
  return [f for f in standard_features] + [f + ["debug"] for f in standard_features]

def _standard_library_runtime_names():
  return depset([_halide_library_runtime_target_name(f) for f in _standard_library_runtime_features()])

def halide_library_runtimes():
  runtime_package = ""
  if PACKAGE_NAME != runtime_package:
    fail("halide_library_runtimes can only be used from package '%s' (this is %s)" % (runtime_package, PACKAGE_NAME))
  unused = [_define_halide_library_runtime(f) for f in _standard_library_runtime_features()]
  unused = unused  # unused variable


def halide_generator(name,
                     srcs,
                     copts=[],
                     deps=[],
                     generator_name="",
                     includes=[],
                     tags=[],
                     visibility=None):
  if not name.endswith(".generator"):
    fail("halide_generator rules must end in .generator")

  if not generator_name:
    generator_name = name[:-10]  # strip ".generator" suffix

  native.cc_library(
      name="%s_library" % name,
      srcs=srcs,
      alwayslink=1,
      copts=copts + halide_language_copts(),
      deps=depset([
          "@halide//:language"
      ] + deps),
      tags=["manual"] + tags,
      visibility=["//visibility:private"])

  native.cc_binary(
      name="%s_binary" % name,
      copts=copts + halide_language_copts(),
      linkopts=halide_language_linkopts(),
      deps=[
          ":%s_library" % name,
          "@halide//:gengen",
      ],
      tags=["manual"] + tags,
      visibility=["//visibility:private"])
  _gengen_closure(
      name="%s_closure" % name,
      generator_binary="%s_binary" % name,
      halide_generator_name=generator_name,
      visibility=["//visibility:private"])

  # If srcs is empty, we're building the halide-library-runtime,
  # which has no stub: just skip it.
  stub_gen_hdrs_target = []
  if srcs:
    # The specific target doesn't matter (much), but we need
    # something that is valid, so uniformly choose first entry
    # so that build product cannot vary by build host
    stub_header_target = _select_multitarget(
        base_target=_HALIDE_TARGET_CONFIG_INFO[0][0],
        halide_target_features=[],
        halide_target_map={})
    _gengen(
        name="%s_stub_gen" % name,
        filename=name[:-10],  # strip ".generator" suffix
        generator_closure=":%s_closure" % name,
        halide_target=stub_header_target,
        sanitizer=select({
            "@halide//:halide_config_asan": "asan",
            "@halide//:halide_config_msan": "msan",
            "@halide//:halide_config_tsan": "tsan",
            "//conditions:default": "",
        }),
        outputs=["cpp_stub"],
        tags=tags,
        visibility=["//visibility:private"])
    stub_gen_hdrs_target = [":%s_stub_gen" % name]

  native.cc_library(
      name=name,
      alwayslink=1,
      hdrs=stub_gen_hdrs_target,
      deps=[
          ":%s_library" % name,
          "@halide//:language"
      ],
      copts=copts + halide_language_copts(),
      includes=includes,
      visibility=visibility,
      tags=["manual"] + tags)


def halide_library_from_generator(name,
                                  generator,
                                  debug_codegen_level=0,
                                  deps=[],
                                  extra_outputs=[],
                                  function_name=None,
                                  generator_args=[],
                                  halide_target_features=[],
                                  halide_target_map=halide_library_default_target_map(),
                                  hexagon_code_signer=None,
                                  includes=[],
                                  namespace=None,
                                  tags=[],
                                  visibility=None):
  if not function_name:
    function_name = name

  if namespace:
    function_name = "%s::%s" % (namespace, function_name)

  # For generator_args, we support both arrays of strings, and space separated strings
  if type(generator_args) != type(""):
    generator_args = " ".join(generator_args);

  # Escape backslashes and double quotes.
  generator_args = generator_args.replace("\\", '\\\\"').replace('"', '\\"')

  if _has_dupes(halide_target_features):
    fail("Duplicate values in halide_target_features: %s" %
         str(halide_target_features))
  if _has_dupes(extra_outputs):
    fail("Duplicate values in extra_outputs: %s" % str(extra_outputs))

  full_halide_target_features = sorted(depset(halide_target_features + ["c_plus_plus_name_mangling", "no_runtime"]).to_list())
  user_halide_target_features = sorted(depset(halide_target_features).to_list())

  if "cpp" in extra_outputs:
    fail("halide_library('%s') doesn't support 'cpp' in extra_outputs; please depend on '%s_cc' instead." % (name, name))

  for san in ["asan", "msan", "tsan"]:
    if san in halide_target_features:
      fail("halide_library('%s') doesn't support '%s' in halide_target_features; please build with --config=%s instead." % (name, san, san))

  generator_closure = "%s_closure" % generator

  outputs = ["static_library", "h"] + extra_outputs

  condition_deps = {}
  condition_hdrs = {}
  for base_target, _, _, _ in _HALIDE_TARGET_CONFIG_INFO:
    multitarget = _select_multitarget(
        base_target=base_target,
        halide_target_features=full_halide_target_features,
        halide_target_map=halide_target_map)
    base_target_name = _halide_target_to_bazel_rule_name(base_target)
    _gengen(
        name="%s_%s" % (base_target_name, name),
        filename="%s/%s" % (base_target_name, name),
        halide_generator_args=generator_args,
        generator_closure=generator_closure,
        halide_target=multitarget,
        halide_function_name=function_name,
        sanitizer=select({
            "@halide//:halide_config_asan": "asan",
            "@halide//:halide_config_msan": "msan",
            "@halide//:halide_config_tsan": "tsan",
            "//conditions:default": "",
        }),
        debug_codegen_level=debug_codegen_level,
        hexagon_code_signer=hexagon_code_signer,
        tags=["manual"] + tags,
        outputs=outputs)
    libname = "halide_internal_%s_%s" % (name, base_target_name)
    native.cc_library(
        name=libname,
        srcs=["%s/%s.a" % (base_target_name, name)],
        hdrs=["%s/%s.h" % (base_target_name, name)],
        tags=["manual"] + tags,
        visibility=["//visibility:private"])
    for s in _config_settings(base_target):
      condition_deps[s] = [":%s" % libname]
      condition_hdrs[s] = ["%s/%s.h" % (base_target_name, name)]

  # Copy the header file so that include paths are correct
  native.genrule(
      name="%s_h" % name,
      srcs=select(condition_hdrs),
      outs=["%s.h" % name],
      cmd="for i in $(SRCS); do cp $$i $(@D); done",
      tags=tags,
      visibility=visibility
  )
  # Create a _cc target for (unusual) applications that want C++ source output;
  # we don't support this via extra_outputs=["cpp"] because it can end up being
  # compiled by Bazel, producing duplicate symbols; also, targets that want this
  # sometimes want to compile it via a separate tool (e.g., XCode to produce
  # certain bitcode variants). Note that this deliberately does not produce
  # a cc_library() output.

  # Use a canonical target to build CC, regardless of config detected
  cc_target = _select_multitarget(
      base_target=_HALIDE_TARGET_CONFIG_INFO[0][0],
      halide_target_features=full_halide_target_features,
      halide_target_map=halide_target_map)
  if len(cc_target) > 1:
    # This can happen if someone uses halide_target_map
    # to force everything to be multitarget. In that
    # case, just use the first entry.
    cc_target = [cc_target[0]]

  _gengen(
      name="%s_cc" % name,
      filename=name,
      halide_generator_args=generator_args,
      generator_closure=generator_closure,
      halide_target=cc_target,
      sanitizer=select({
          "@halide//:halide_config_asan": "asan",
          "@halide//:halide_config_msan": "msan",
          "@halide//:halide_config_tsan": "tsan",
          "//conditions:default": "",
      }),
      halide_function_name=function_name,
      outputs=["cpp"],
      tags=["manual"] + tags)

  runtime_library = _halide_library_runtime_target_name(user_halide_target_features)
  if runtime_library in _standard_library_runtime_names().to_list():
    runtime_library = "@halide//:%s" % runtime_library
  else:
    if not native.existing_rule(runtime_library):
      _define_halide_library_runtime(user_halide_target_features)
      # Note to maintainers: if this message is reported, you probably want to add
      # feature combination as an item in _standard_library_runtime_features()
      # in this file. (Failing to do so will only cause potentially-redundant
      # runtime library building, but no correctness problems.)
      print("\nCreating Halide runtime library for feature set combination: " +
            str(_discard_useless_features(user_halide_target_features)) + "\n" +
            "If you see this message, there is no need to take any action; " +
            "however, please forward this message to halide-dev@lists.csail.mit.edu " +
            "so that we can include this case to reduce build times.")
    runtime_library = ":%s" % runtime_library

  native.cc_library(
      name=name,
      hdrs=[":%s_h" % name],
      # Order matters: runtime_library must come *after* condition_deps, so that
      # they will be presented to the linker in this order, and we want
      # unresolved symbols in the generated code (in condition_deps) to be
      # resolved in the runtime library.
      deps=select(condition_deps) + deps + ["@halide//:runtime", runtime_library],
      includes=includes,
      tags=tags,
      visibility=visibility)

  # Although "#include SOME_MACRO" is legal C/C++, it doesn't work in all environments.
  # So, instead, we'll make a local copy of the .cpp file and use sed to
  # put the include path in directly.
  native.genrule(
      name = "%s_RunGenStubs" % name,
      srcs = [ "@halide//:tools/RunGenStubs.cpp" ],
      cmd = "cat $(location @halide//:tools/RunGenStubs.cpp) | " +
            "sed -e 's|HL_RUNGEN_FILTER_HEADER|\"%s%s%s.h\"|g' > $@" % (PACKAGE_NAME, "/" if PACKAGE_NAME else "", name),
      outs = [ "%s_RunGenStubs.cpp" % name, ],
      tags=["manual", "notap"] + tags,
      visibility=["//visibility:private"]
  )

  # Note that the .rungen targets are tagged as manual+notap, as some
  # extant Generators don't (yet) have the proper generator_deps
  # or filter_deps configured. ("notap" is used internally by
  # certain Google test systems; it is ignored in public Bazel builds.)
  #
  # (Of course, this requires that we have some explicit build-and-link tests
  # elsewhere to verify that at least some expected-to-work Generators
  # stay working.)
  native.cc_binary(
      name="%s.rungen" % name,
      srcs=[":%s_RunGenStubs" % name],
      deps=[
          "@halide//:rungen",
          ":%s" % name,
      ],
      tags=["manual", "notap"] + tags,
      visibility=["//visibility:private"])

  # Return the fully-qualified built target name.
  return "//%s:%s" % (PACKAGE_NAME, name)

def halide_library(name,
                   srcs,
                   copts=[],
                   debug_codegen_level=0,
                   extra_outputs=[],  # "stmt" and/or "assembly" are useful for debugging
                   filter_deps=[],
                   function_name=None,
                   generator_args=[],
                   generator_deps=[],
                   generator_name=None,
                   halide_target_features=[],
                   halide_target_map=halide_library_default_target_map(),
                   hexagon_code_signer=None,
                   includes=[],
                   namespace=None,
                   visibility=None):
  halide_generator(
      name="%s.generator" % name,
      srcs=srcs,
      generator_name=generator_name,
      deps=generator_deps,
      includes=includes,
      copts=copts,
      visibility=visibility)

  return halide_library_from_generator(
      name=name,
      generator=":%s.generator" % name,
      deps=filter_deps,
      visibility=visibility,
      namespace=namespace,
      includes=includes,
      function_name=function_name,
      generator_args=generator_args,
      debug_codegen_level=debug_codegen_level,
      halide_target_features=halide_target_features,
      halide_target_map=halide_target_map,
      hexagon_code_signer=hexagon_code_signer,
      extra_outputs=extra_outputs)

back to top