load(
    ":build_defs.bzl",
    "CLDEBUGINFO100_GRAMMAR_JSON_FILE",
    "COMMON_COPTS",
    "DEBUGINFO_GRAMMAR_JSON_FILE",
    "SHDEBUGINFO100_GRAMMAR_JSON_FILE",
    "TEST_COPTS",
    "create_grammar_tables_target",
    "ExtInst",
    "generate_extinst_lang_headers",
    "incompatible_with",
)

package(
    default_visibility = ["//visibility:private"],
    features = [
        "layering_check",
    ],
)

licenses(["notice"])

exports_files([
    "CHANGES",
    "LICENSE",
])

py_binary(
    # The script that generates compressed grammar tables for
    # instructions and operands.
    name = "ggt",
    main = "utils/ggt.py", # The file found by $(location :ggt)
    srcs = [
        "utils/ggt.py",
        "utils/Table/__init__.py",
        "utils/Table/Context.py",
        "utils/Table/IndexRange.py",
        "utils/Table/Operand.py",
        "utils/Table/StringList.py",
    ],
)

py_binary(
    name = "generate_language_headers",
    srcs = ["utils/generate_language_headers.py"],
)

create_grammar_tables_target(
    name="core", # unused
    extinsts = [
        ExtInst("glsl.std.450", target="spirv_glsl_grammar_unified1"),
        ExtInst("opencl.std.100", target="spirv_opencl_grammar_unified1"),
        ExtInst("opencl.debuginfo.100", prefix="CLDEBUG100_"),
        ExtInst("nonsemantic.shader.debuginfo.100", prefix="SHDEBUG100_"),
        ExtInst("tosa.001000.1", target="spirv_ext_inst_tosa_001000_1", prefix="TOSA_"),
        ExtInst("arm.motion-engine.100", target="spirv_ext_inst_arm_motion_engine_100"),
    ] + [ExtInst(e) for e in [
            "spv-amd-shader-explicit-vertex-parameter",
            "spv-amd-shader-trinary-minmax",
            "spv-amd-gcn-shader",
            "spv-amd-shader-ballot",
            "debuginfo",
            "nonsemantic.clspvreflection",
            "nonsemantic.vkspreflection",
        ]
    ]
)

generate_extinst_lang_headers(
    name = "DebugInfo",
    grammar = DEBUGINFO_GRAMMAR_JSON_FILE,
)

generate_extinst_lang_headers(
    name = "OpenCLDebugInfo100",
    grammar = CLDEBUGINFO100_GRAMMAR_JSON_FILE,
)

generate_extinst_lang_headers(
    name = "NonSemanticShaderDebugInfo100",
    grammar = SHDEBUGINFO100_GRAMMAR_JSON_FILE,
)

py_binary(
    name = "generate_registry_tables",
    srcs = ["utils/generate_registry_tables.py"],
)

genrule(
    name = "generators_inc",
    srcs = ["@spirv_headers//:spirv_xml_registry"],
    outs = ["generators.inc"],
    cmd = "$(location :generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
    cmd_bat = "$(location :generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)",
    tools = [":generate_registry_tables"],
)

py_binary(
    name = "update_build_version",
    srcs = ["utils/update_build_version.py"],
)

genrule(
    name = "build_version_inc",
    srcs = ["CHANGES"],
    outs = ["build-version.inc"],
    cmd = "SOURCE_DATE_EPOCH=0 $(location :update_build_version) $(location CHANGES) $(location build-version.inc)",
    cmd_bat = "set SOURCE_DATE_EPOCH=0  && $(location :update_build_version) $(location CHANGES) $(location build-version.inc)",
    tools = [":update_build_version"],
)

# Libraries

cc_library(
    name = "spirv_tools",
    hdrs = [
        "include/spirv-tools/libspirv.h",
        "include/spirv-tools/libspirv.hpp",
    ],
    copts = COMMON_COPTS,
    includes = ["include"],
    linkstatic = 1,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
    ],
)

cc_library(
    name = "spirv_tools_internal",
    srcs = glob([
        "source/*.cpp",
        "source/util/*.cpp",
        "source/val/*.cpp",
    ], exclude = [
        "source/mimalloc.cpp"
    ]) + [
        ":build_version_inc",
        ":gen_compressed_tables",
        ":gen_extinst_lang_headers_DebugInfo",
        ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100",
        ":gen_extinst_lang_headers_OpenCLDebugInfo100",
        ":generators_inc",
    ],
    hdrs = [
        "include/spirv-tools/libspirv.h",
        "include/spirv-tools/libspirv.hpp",
        ":gen_extinst_lang_headers_DebugInfo",
        ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100",
        ":gen_extinst_lang_headers_OpenCLDebugInfo100",
    ] + glob([
        "source/*.h",
        "source/util/*.h",
        "source/val/*.h",
    ]),
    copts = COMMON_COPTS,
    includes = ["include"],
    deps = [
        "@spirv_headers//:spirv_common_headers",
        "@spirv_headers//:spirv_cpp11_headers",
    ],
)

cc_library(
    name = "spirv_tools_opt",
    hdrs = [
        "include/spirv-tools/optimizer.hpp",
    ],
    copts = COMMON_COPTS,
    linkstatic = 1,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools",
        ":spirv_tools_opt_internal",
    ],
)

cc_library(
    name = "spirv_tools_opt_internal",
    srcs = glob(["source/opt/*.cpp"]),
    hdrs = glob(["source/opt/*.h"]) + [
        "include/spirv-tools/optimizer.hpp",
    ],
    copts = COMMON_COPTS,
    deps = [
        ":spirv_tools_internal",
        "@spirv_headers//:spirv_common_headers",
        "@spirv_headers//:spirv_c_headers",
    ],
)

cc_library(
    name = "spirv_tools_reduce",
    srcs = glob(["source/reduce/*.cpp"]),
    hdrs = glob(["source/reduce/*.h"]),
    copts = COMMON_COPTS,
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
    ],
)

cc_library(
    name = "spirv_tools_link",
    srcs = glob(["source/link/*.cpp"]),
    hdrs = ["include/spirv-tools/linker.hpp", "source/link/fnvar.h"],
    copts = COMMON_COPTS,
    linkstatic = 1,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
    ],
)

cc_library(
    name = "spirv_tools_lint_internal",
    srcs = glob([
        "source/lint/*.cpp",
        "source/lint/*.h",
    ]),
    hdrs = ["include/spirv-tools/linter.hpp"] + glob([
        "source/lint/*.h",
    ]),
    copts = COMMON_COPTS,
    includes = ["include"],
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
    ],
)

cc_library(
    name = "spirv_tools_lint",
    hdrs = ["include/spirv-tools/linter.hpp"],
    copts = COMMON_COPTS,
    includes = ["include"],
    linkstatic = 1,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools",
        ":spirv_tools_lint_internal",
    ],
)

cc_library(
    name = "tools_util",
    srcs = glob(["tools/util/*.cpp"]),
    hdrs = glob(["tools/util/*.h"]),
    copts = COMMON_COPTS,
    deps = [":spirv_tools"],
)

cc_library(
    name = "tools_io",
    hdrs = ["tools/io.h"],
    srcs = ["tools/io.cpp"],
    copts = COMMON_COPTS,
)

# Tools

cc_binary(
    name = "spirv-as",
    srcs = [
        "tools/as/as.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":tools_io",
        ":tools_util",
    ],
)

cc_binary(
    name = "spirv-dis",
    srcs = [
        "tools/dis/dis.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools",
        ":tools_io",
        ":tools_util",
    ],
)

cc_binary(
    name = "spirv-objdump",
    srcs = [
        "tools/objdump/extract_source.cpp",
        "tools/objdump/extract_source.h",
        "tools/objdump/objdump.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
        ":tools_io",
        ":tools_util",
        "@spirv_headers//:spirv_cpp_headers",
    ],
)

cc_binary(
    name = "spirv-val",
    srcs = [
        "tools/val/val.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":tools_io",
        ":tools_util",
    ],
)

cc_binary(
    name = "spirv-opt",
    srcs = [
        "tools/opt/opt.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
        ":tools_io",
        ":tools_util",
    ],
)

cc_binary(
    name = "spirv-reduce",
    srcs = [
        "tools/reduce/reduce.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
        ":spirv_tools_reduce",
        ":tools_io",
        ":tools_util",
    ],
)

cc_binary(
    name = "spirv-link",
    srcs = [
        "tools/link/linker.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_link",
        ":tools_io",
        ":tools_util",
    ],
)

cc_binary(
    name = "spirv-lint",
    srcs = [
        "tools/lint/lint.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_lint",
        ":spirv_tools_opt_internal",
        ":tools_io",
        ":tools_util",
    ],
)

cc_binary(
    name = "spirv-cfg",
    srcs = [
        "tools/cfg/bin_to_dot.cpp",
        "tools/cfg/bin_to_dot.h",
        "tools/cfg/cfg.cpp",
    ],
    copts = COMMON_COPTS,
    visibility = ["//visibility:public"],
    deps = [
        ":spirv_tools_internal",
        ":tools_io",
        ":tools_util",
    ],
)

# Unit tests

cc_library(
    name = "test_lib",
    testonly = 1,
    srcs = [
        "test/unit_spirv.cpp",
    ],
    hdrs = [
        "test/test_fixture.h",
        "test/unit_spirv.h",
    ],
    copts = TEST_COPTS,
    deps = [
        ":spirv_tools_internal",
        "@googletest//:gtest",
    ],
)

# PCH (precompiled header) tests only work when using CMake and MSVC on Windows,
# so they will be skipped in the Bazel builds.

[cc_test(
    name = "base_{testcase}_test".format(testcase = f[len("test/"):-len("_test.cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS + ["-DTESTING"],
    linkstatic = 1,
    target_compatible_with = {
        "test/timer_test.cpp": incompatible_with(["@bazel_tools//src/conditions:windows"]),
    }.get(f, []),
    deps = [
        "tools_util",
        ":spirv_tools_internal",
        ":test_lib",
        ":tools_io",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
        "@spirv_headers//:spirv_common_headers",
    ],
) for f in glob(
    [
        "test/*_test.cpp",
        "test/tools/*_test.cpp",
    ],
    exclude = [
        "test/cpp_interface_test.cpp",
        "test/pch_test.cpp",
    ],
)]

cc_test(
    name = "base_cpp_interface_test",
    size = "small",
    srcs = ["test/cpp_interface_test.cpp"],
    linkstatic = 1,
    deps = [
        ":spirv_tools_opt_internal",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
        "@spirv_headers//:spirv_cpp11_headers",
    ],
)

cc_test(
    name = "base_ilist_test",
    size = "small",
    srcs = ["test/util/ilist_test.cpp"],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":spirv_tools_internal",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
)

cc_library(
    name = "link_test_lib",
    testonly = 1,
    hdrs = ["test/link/linker_fixture.h"],
    copts = TEST_COPTS,
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_link",
        ":test_lib",
        "@effcee//:effcee",
        "@re2//:re2",
    ],
)

[cc_test(
    name = "link_{testcase}_test".format(testcase = f[len("test/link/"):-len("_test.cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":link_test_lib",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
) for f in glob(
    ["test/link/*_test.cpp"],
)]

[cc_test(
    name = "lint_{testcase}_test".format(testcase = f[len("test/lint/"):-len("_test.cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":spirv_tools",
        ":spirv_tools_lint_internal",
        ":spirv_tools_opt_internal",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
) for f in glob(
    ["test/lint/*_test.cpp"],
)]

cc_library(
    name = "opt_test_lib",
    testonly = 1,
    srcs = [
        "test/opt/pass_utils.cpp",
    ],
    hdrs = [
        "test/opt/assembly_builder.h",
        "test/opt/function_utils.h",
        "test/opt/module_utils.h",
        "test/opt/pass_fixture.h",
        "test/opt/pass_utils.h",
    ],
    copts = TEST_COPTS,
    deps = [
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
        "@effcee//:effcee",
        "@googletest//:gtest",
    ],
)

[cc_test(
    name = "opt_{testcase}_test".format(testcase = f[len("test/opt/"):-len("_test.cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":opt_test_lib",
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
        ":test_lib",
        "@effcee//:effcee",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
) for f in glob(["test/opt/*_test.cpp"])]

[cc_test(
    name = "opt_dom_tree_{testcase}_test".format(testcase = f[len("test/opt/dominator_tree/"):-len(".cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":opt_test_lib",
        ":spirv_tools_opt_internal",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
) for f in glob(
    ["test/opt/dominator_tree/*.cpp"],
    exclude = ["test/opt/dominator_tree/pch_test_opt_dom.cpp"],
)]

[cc_test(
    name = "opt_loop_{testcase}_test".format(testcase = f[len("test/opt/loop_optimizations/"):-len(".cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":opt_test_lib",
        ":spirv_tools",
        ":spirv_tools_opt_internal",
        "@effcee//:effcee",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
) for f in glob(
    ["test/opt/loop_optimizations/*.cpp"],
    exclude = ["test/opt/loop_optimizations/pch_test_opt_loop.cpp"],
)]

cc_library(
    name = "reduce_test_lib",
    testonly = 1,
    srcs = [
        "test/reduce/reduce_test_util.cpp",
    ],
    hdrs = ["test/reduce/reduce_test_util.h"],
    copts = TEST_COPTS,
    deps = [
        ":spirv_tools",
        ":spirv_tools_opt_internal",
        ":spirv_tools_reduce",
        ":test_lib",
        ":tools_io",
        "@googletest//:gtest",
    ],
)

[cc_test(
    name = "reduce_{testcase}_test".format(testcase = f[len("test/reduce/"):-len("_test.cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":reduce_test_lib",
        ":spirv_tools_internal",
        ":spirv_tools_opt_internal",
        ":spirv_tools_reduce",
        "@googletest//:gtest_main",
    ],
) for f in glob(["test/reduce/*_test.cpp"])]

[cc_test(
    name = "util_{testcase}_test".format(testcase = f[len("test/util/"):-len("_test.cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":spirv_tools_internal",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
) for f in glob(["test/util/*_test.cpp"])]

cc_library(
    name = "val_test_lib",
    testonly = 1,
    srcs = [
        "test/val/val_code_generator.cpp",
    ],
    hdrs = [
        "test/val/val_code_generator.h",
        "test/val/val_fixtures.h",
    ],
    copts = TEST_COPTS,
    deps = [
        ":spirv_tools_internal",
        ":test_lib",
    ],
)

[cc_test(
    name = "val_{testcase}_test".format(testcase = f[len("test/val/val_"):-len("_test.cpp")]),
    size = "small",
    srcs = [f],
    copts = TEST_COPTS,
    linkstatic = 1,
    deps = [
        ":spirv_tools_internal",
        ":test_lib",
        ":val_test_lib",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
) for f in glob(
    ["test/val/val_*_test.cpp"],
    exclude = [
        "test/val/val_capability_test.cpp",
        "test/val/val_limits_test.cpp",
    ],
)]

cc_test(
    name = "val_capability_test",
    size = "large",
    timeout = "long",
    srcs = ["test/val/val_capability_test.cpp"],
    copts = TEST_COPTS + ["-O3"],
    linkstatic = 1,
    deps = [
        ":spirv_tools_internal",
        ":test_lib",
        ":val_test_lib",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
)

cc_test(
    name = "val_limits_test",
    size = "large",
    timeout = "long",
    srcs = ["test/val/val_limits_test.cpp"],
    copts = TEST_COPTS + [
        "-O3",
    ],
    linkstatic = 1,
    deps = [
        ":test_lib",
        ":val_test_lib",
        "@googletest//:gtest",
        "@googletest//:gtest_main",
    ],
)
