summaryrefslogtreecommitdiff
path: root/kotlin/internal/jvm/compile.bzl
blob: 465c73035ecb6e00b106776d9f46436bc17dfb48 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# Copyright 2018 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.
load("//kotlin/internal:defs.bzl", "KtInfo", "TOOLCHAIN_TYPE")
load("//kotlin/internal/jvm:plugins.bzl", "plugins")
load("//kotlin/internal/common:common.bzl", "common")

# MISC UTILS ###########################################################################################################
def _partition_srcs(srcs):
    kt_srcs = []
    java_srcs = []
    src_jars = []

    for f in srcs:
        if f.path.endswith(".kt"):
            kt_srcs.append(f)
        elif f.path.endswith(".java"):
            java_srcs.append(f)
        elif f.path.endswith(".srcjar"):
            src_jars.append(f)

    kt = depset(kt_srcs)
    java = depset(java_srcs)

    return struct (
        kt = kt,
        java = java,
        all_srcs = kt + java,
        src_jars = depset(src_jars)
    )

# JAR ACTIONS ##########################################################################################################
def _fold_jars_action(ctx, rule_kind, output_jar, input_jars):
    args=[
        "--normalize",
        "--compression",
        "--deploy_manifest_lines",
            "Target-Label: %s" % str(ctx.label),
            "Injecting-Rule-Kind: %s" % rule_kind,
        "--output", output_jar.path
    ]
    for i in input_jars:
        args += ["--sources", i.path]
    ctx.action(
        mnemonic = "KotlinFoldOutput",
        inputs = input_jars,
        outputs = [output_jar],
        executable = ctx.executable._singlejar,
        arguments = args,
        progress_message="Merging Kotlin output jar " + output_jar.short_path
    )

_CONVENTIONAL_RESOURCE_PATHS = [
    "src/main/resources",
    "src/test/resources",
]

def _adjust_resources_path_by_strip_prefix(path, resource_strip_prefix):
    if not path.startswith(resource_strip_prefix):
      fail("Resource file %s is not under the specified prefix to strip" % path)

    clean_path = path[len(resource_strip_prefix):]
    return resource_strip_prefix, clean_path

def _adjust_resources_path_by_default_prefixes(path):
    for cp in _CONVENTIONAL_RESOURCE_PATHS:
        dir_1, dir_2, rel_path = path.partition(cp)
        if rel_path:
            return  dir_1 + dir_2, rel_path
    return "", path

def _adjust_resources_path(path, resource_strip_prefix):
    if resource_strip_prefix:
      return _adjust_resources_path_by_strip_prefix(path,resource_strip_prefix)
    else:
      return _adjust_resources_path_by_default_prefixes(path)

def _add_resources_cmd(ctx):
    res_cmd = []
    for f in ctx.files.resources:
        c_dir, res_path = _adjust_resources_path(f.short_path, ctx.attr.resource_strip_prefix)
        target_path = res_path
        if target_path[0] == "/":
            target_path = target_path[1:]
        line = "{target_path}={c_dir}{res_path}\n".format(
            res_path=res_path,
            target_path=target_path,
            c_dir=c_dir)
        res_cmd.extend([line])
    return "".join(res_cmd)

def _build_resourcejar_action(ctx):
    resources = _add_resources_cmd(ctx)
    resources_jar_output = ctx.actions.declare_file(ctx.label.name + "-resources.jar")
    zipper_arg_path = ctx.actions.declare_file("%s_resources_zipper_args" % ctx.label.name)
    ctx.file_action(zipper_arg_path, resources)
    cmd = """
rm -f {resources_jar_output}
{zipper} c {resources_jar_output} @{path}
""".format(
        path=zipper_arg_path.path,
        resources_jar_output=resources_jar_output.path,
        zipper=ctx.executable._zipper.path,
    )
    ctx.action(
        mnemonic="KotlinZipResourceJar",
        inputs=ctx.files.resources + [ctx.executable._zipper,zipper_arg_path],
        outputs=[resources_jar_output],
        command=cmd,
        progress_message="Creating intermediate resource jar %s" % ctx.label,
        arguments=[]
    )
    return resources_jar_output

def kt_jvm_compile_action(ctx, rule_kind, output_jar, srcs):
    """This macro performs a compile operation in a single action.

    Args:
      rule_kind: The rule kind,
      output_jar: The jar file that this macro will use as the output of the action.
      module_name: The Kotlin module name, this must be provided and is used by the compiler for symbol mangling in
         advanced use cases.
      compile_jars: The compile time jars provided on the classpath for the compile operations -- callers are
        responsible for preparing the classpath. The stdlib (and jdk7 + jdk8) should generally be added to the classpath
        by the caller -- kotlin-reflect could be optional.
      friend_paths: A list of jars paths that this compilation unit should have package private access to.
      srcs: a struct with the various input sources partitioned.
    """
    toolchain=ctx.toolchains[TOOLCHAIN_TYPE]

    friends=getattr(ctx.attr, "friends", [])
    deps = [d[JavaInfo] for d in friends + ctx.attr.deps] + [toolchain.jvm_stdlibs]
    compile_jars = java_common.merge(deps).compile_jars

    if len(friends) == 0:
        module_name=common.derive_module_name(ctx)
        friend_paths=depset()
    elif len(friends) == 1:
        if friends[0][KtInfo] == None:
            fail("only kotlin dependencies can be friends")
        elif ctx.attr.module_name:
            fail("if friends has been set then module_name cannot be provided")
        else:
            friend_paths=depset([j.path for j in friends[0][JavaInfo].compile_jars])
            module_name = friends[0][KtInfo].module_name
    else:
        fail("only one friend is possible")

    classes_directory=common.declare_output_directory(ctx, "jvm", "classes")
    generated_classes_directory=common.declare_output_directory(ctx, "jvm", "generated_classes")
    sourcegen_directory=common.declare_output_directory(ctx, "jvm", "sourcegenfiles")
    temp_directory=common.declare_output_directory(ctx, "jvm", "temp")

    args = common.init_args(ctx, rule_kind, module_name)

    args.add("--classdir", classes_directory)
    args.add("--sourcegendir", sourcegen_directory)
    args.add("--tempdir", temp_directory)
    args.add("--kotlin_generated_classdir", generated_classes_directory)

    args.add("--output", output_jar)
    args.add("--kotlin_output_jdeps", ctx.outputs.jdeps)
    args.add("--kotlin_output_srcjar", ctx.outputs.srcjar)

    args.add("--kotlin_friend_paths", "\n".join(friend_paths.to_list()))

    args.add("--classpath", compile_jars)
    args.add_all("--sources", srcs.all_srcs, omit_if_empty=True)
    args.add_all("--source_jars", srcs.src_jars, omit_if_empty=True)

    # Collect and prepare plugin descriptor for the worker.
    plugin_info=plugins.merge_plugin_infos(ctx.attr.plugins + ctx.attr.deps)
    if len(plugin_info.annotation_processors) > 0:
        args.add("--kotlin_plugins", plugin_info.to_json())

    progress_message = "Compiling Kotlin %s { kt: %d, java: %d, srcjars: %d }" % (
        ctx.label,
        len(srcs.kt),
        len(srcs.java),
        len(srcs.src_jars)
    )

    inputs, _, input_manifests = ctx.resolve_command(tools = [toolchain.kotlinbuilder])
    ctx.actions.run(
        mnemonic = "KotlinCompile",
        inputs = depset(inputs) + ctx.files.srcs + compile_jars,
        outputs = [
            output_jar,
            ctx.outputs.jdeps,
            ctx.outputs.srcjar,
            sourcegen_directory,
            classes_directory,
            temp_directory,
            generated_classes_directory
        ],
        executable = toolchain.kotlinbuilder.files_to_run.executable,
        execution_requirements = {"supports-workers": "1"},
        arguments = [args],
        progress_message = progress_message,
        input_manifests = input_manifests
    )

    # create the java provider and the kotlin provider. Whilst a struct is being returned, and this is a valid way of
    # creating a provider, it is intended that the client transforms this into an form.
    return struct(
        java=JavaInfo(
            output_jar = ctx.outputs.jar,
            compile_jar = ctx.outputs.jar,
            source_jar = ctx.outputs.srcjar,
        #  jdeps = ctx.outputs.jdeps,
            deps = deps,
            runtime_deps = [d[JavaInfo] for d in ctx.attr.runtime_deps],
            exports = [d[JavaInfo] for d in getattr(ctx.attr, "exports", [])],
            neverlink = getattr(ctx.attr, "neverlink", False)
        ),
        kt=KtInfo(
            srcs=ctx.files.srcs,
            module_name = module_name,
            # intelij aspect needs this.
            outputs = struct(
                jdeps = ctx.outputs.jdeps,
                jars = [struct(
                  class_jar = ctx.outputs.jar,
                  ijar = None,
                  source_jars = [ctx.outputs.srcjar]
                )]
            ),
        )
    )

def kt_jvm_produce_jar_actions(ctx, rule_kind, src_jars=[]):
    """Setup a kotlin compile action. This method takes care of all of the aspects of producing a jar.

    Specifically this action will conditionally set up actions to fold resources and resourcejars and merge them onto a
    jar compiled by the builder. It indirects the output_jar -- i.e., if no resources or resource jars are present it
    won't do anything.

    Args:
        ctx: The rule context.
    Returns:
        A JavaInfo struct for the output jar that this macro will build.
    """
    # The main output jars
    output_jar = ctx.outputs.jar

    # The output of the compile step may be combined (folded) with other entities -- e.g., other class files from annotation processing, embedded resources.
    kt_compile_output_jar=output_jar
    # the list of jars to merge into the final output, start with the resource jars if any were provided.
    output_merge_list=ctx.files.resource_jars

    # If this rule has any resources declared setup a zipper action to turn them into a jar and then add the declared zipper output to the merge list.
    if len(ctx.files.resources) > 0:
        output_merge_list = output_merge_list + [_build_resourcejar_action(ctx)]

    # If this compile operation requires merging other jars setup the compile operation to go to a intermediate file and add that file to the merge list.
    if len(output_merge_list) > 0:
        # Intermediate jar containing the Kotlin compile output.
        kt_compile_output_jar=ctx.new_file(ctx.label.name + "-ktclass.jar")
        # If we setup indirection than the first entry in the merge list is the result of the kotlin compile action.
        output_merge_list=[ kt_compile_output_jar ] + output_merge_list

    srcs = _partition_srcs(ctx.files.srcs)

    if (len(srcs.kt) + len(srcs.java) == 0) and len(srcs.src_jars) == 0:
        fail("no sources provided")

    # setup the merge action if needed.
    if len(output_merge_list) > 0:
        _fold_jars_action(ctx, rule_kind, output_jar, output_merge_list)

    # setup the compile action.
    return kt_jvm_compile_action(
        ctx,
        rule_kind = rule_kind,
        output_jar = kt_compile_output_jar,
        srcs = srcs
    )