Adding feature file generation via Bazel

Change-Id: Ifa9cdf01c3129e5cd6cb7b8f4aee956eef9055f8
diff --git a/BUILD b/BUILD
index 2b0b646..47279ae 100644
--- a/BUILD
+++ b/BUILD
@@ -300,7 +300,20 @@
 APPS = ONOS_DRIVERS + ONOS_PROVIDERS + ONOS_APPS + MODELS + PIPELINES + \
        PROTOCOL_APPS
 
+FEATURES = [
+    '//features:onos-thirdparty-base',
+    '//features:onos-thirdparty-web',
+    '//features:onos-api',
+    '//features:onos-core',
+#    '//features:onos-incubator',
+#    '//features:onos-rest',
+#    '//features:onos-gui',
+#    '//features:onos-gui2',
+#    '//features:onos-cli',
+#    '//features:onos-security',
+ ]
+
 filegroup(
     name = "onos",
-    srcs = CORE + APPS,
+    srcs = CORE + APPS + FEATURES,
 )
diff --git a/features/BUILD b/features/BUILD
new file mode 100644
index 0000000..b1aca0e
--- /dev/null
+++ b/features/BUILD
@@ -0,0 +1,164 @@
+osgi_feature (
+    name = "onos-thirdparty-base",
+    description = "ONOS 3rd party dependencies",
+    required_features = [],
+    included_bundles = [
+        "@atomix//jar",
+        "@commons_lang//jar",
+        "@commons_lang3//jar",
+        "@commons_text//jar",
+        "@commons_configuration//jar",
+        "@guava//jar",
+        "@netty//jar",
+        "@netty_common//jar",
+        "@netty_buffer//jar",
+        "@netty_transport//jar",
+        "@netty_handler//jar",
+        "@netty_codec//jar",
+        "@netty_transport_native_epoll//jar",
+        "@netty_resolver//jar",
+        "@commons_pool//jar",
+        "@commons_math3//jar",
+        "@joda_time//jar",
+        "@metrics_core//jar",
+        "@metrics_json//jar",
+        "@minimal_json//jar",
+        "@kryo//jar",
+        "@reflectasm//jar",
+        "@asm//jar",
+        "@minlog//jar",
+        "@objenesis//jar",
+        "@jackson_core//jar",
+        "@jackson_annotations//jar",
+        "@jackson_databind//jar",
+        "@commons_collections//jar",
+        "@typesafe_config//jar",
+        "@concurrent_trees//jar",
+        "@commons_io//jar",
+        "@jersey_client//jar",
+        "@mapdb//jar",
+        "@bcpkix_jdk15on//jar",
+        "@bcprov_jdk15on//jar",
+        "@commons_jxpath//jar",
+        "@commons_beanutils//jar",
+        "@jdom//jar",
+        "@sigar//jar",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+osgi_feature (
+    name = "onos-thirdparty-web",
+    description = "ONOS 3rd party dependencies for web apps",
+    required_features = [ "war", "onos-thirdparty-base" ],
+    included_bundles = [
+        "@jersey_common//jar",
+        "@javax_annotation_api//jar",
+        "@javax_ws_rs_api//jar",
+        "@hk2_api//jar",
+        "@hk2_locator//jar",
+        "@hk2_osgi_resource_locator//jar",
+        "@javax_inject//jar",
+        "@jersey_server//jar",
+        "@jersey_hk2//jar",
+        "@aopalliance_repackaged//jar",
+        "@hk2_utils//jar",
+        "@validation_api//jar",
+        "@javassist//jar",
+        "@jersey_container_servlet//jar",
+        "@jersey_container_servlet_core//jar",
+        "@jersey_media_multipart//jar",
+        "@mimepull//jar",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+osgi_feature (
+    name = "onos-api",
+    description = "ONOS services and model API",
+    required_features = ["scr", "onos-thirdparty-base"],
+    included_bundles =[
+         "//utils/misc:onlab-misc",
+         "//utils/osgi:onlab-osgi",
+         "//core/api:onos-api",
+         "//incubator/api:onos-incubator-api",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+osgi_feature (
+    name = "onos-core",
+    description = "ONOS core components",
+    required_features = ["onos-api"],
+    included_bundles = [
+        "//core/net:onos-core-net",
+        "//core/common:onos-core-common",
+        "//core/store/dist:onos-core-dist",
+#        "//core/store/primitives:onos-core-primitives",
+#        "//core/store/persistence:onos-core-persistence",
+#        "//core/store/serializers:onos-core-serializers",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+#osgi_feature (
+#  name = "onos-incubator",
+#  description = "ONOS core incubator components",
+#  required_features = ["onos-core"],
+#  included_bundles = [
+#    "//incubator/net:onos-incubator-net",
+#    "//incubator/store:onos-incubator-store",
+#    "//incubator/rpc:onos-incubator-rpc",
+#  ]
+#)
+#
+#osgi_feature (
+#  name = "onos-rest",
+#  description = "ONOS REST API components",
+#  required_features = ["onos-api", "onos-thirdparty-web"],
+#  included_bundles = [
+#    "//utils/rest:onlab-rest",
+#    "//web/api:onos-rest",
+#  ]
+#)
+#
+#osgi_feature (
+#  name = "onos-gui",
+#  description = "ONOS GUI console components",
+#  required_features = ["onos-api", "onos-thirdparty-web"],
+#  included_bundles = [
+#    "//lib:jetty_websocket",
+#    "//utils/rest:onlab-rest",
+#    "//web/gui:onos-gui",
+#  ]
+#)
+#
+#osgi_feature (
+#  name = "onos-gui2",
+#  description = "ONOS GUI2 console components",
+#  required_features = ["onos-api", "onos-thirdparty-web"],
+#  included_bundles = [
+#    "//lib:jetty_websocket",
+#    "//utils/rest:onlab-rest",
+#    "//web/gui2:onos-gui2",
+#  ]
+#)
+#
+#osgi_feature (
+#  name = "onos-cli",
+#  description ="ONOS admin command console components",
+#  required_features = ["onos-api"],
+#  included_bundles = [
+#    "//cli:onos-cli",
+#  ]
+#)
+#
+#osgi_feature (
+#  name = "onos-security",
+#  description ="Security-Mode ONOS",
+#  required_features = ["onos-api"],
+#  included_bundles = [
+#    "//lib:org.apache.felix.framework.security",
+#    "//core/security:onos-security",
+#  ]
+#)
diff --git a/tools/build/bazel/BUILD b/tools/build/bazel/BUILD
index 6c4e6ec..faaf6bc 100644
--- a/tools/build/bazel/BUILD
+++ b/tools/build/bazel/BUILD
@@ -6,21 +6,21 @@
 )
 
 py_binary(
-    name = "onos-app-oar",
+    name = "onos_app_oar",
     srcs = ["onos_oar.py"],
     main = "onos_oar.py",
     visibility = ["//visibility:public"],
 )
 
 py_binary(
-    name = "onos-stage",
+    name = "onos_stage",
     srcs = ["onos_stage.py"],
     main = "onos_stage.py",
     visibility = ["//visibility:public"],
 )
 
 py_binary(
-    name = "onos-feature",
+    name = "onos_feature",
     srcs = ["onos_feature.py"],
     main = "onos_feature.py",
     visibility = ["//visibility:public"],
diff --git a/tools/build/bazel/onos_app.py b/tools/build/bazel/onos_app.py
index bbb053c..ebb6bb7 100755
--- a/tools/build/bazel/onos_app.py
+++ b/tools/build/bazel/onos_app.py
@@ -134,11 +134,18 @@
     output += APP_FOOTER
     return output
 
+def write(name, msg):
+    if name is not None:
+        with open(name, "w") as file:
+            file.write(msg)
+    else:
+        print msg
 
 if __name__ == '__main__':
     import sys, optparse
 
     parser = optparse.OptionParser()
+    parser.add_option("-O", "--output",   dest="output",       help="Output file")
     parser.add_option("-n", "--name",     dest="feature_coords", help="Feature MVN Coords")
     parser.add_option("-a", "--app",      dest="app_name",     help="App Name")
     parser.add_option("-o", "--origin",   dest="origin",       help="Origin")
@@ -206,14 +213,15 @@
                               **values)
 
     if options.write_feature:
-        print feature
+        write(options.output, feature)
 
     if options.write_features:
-        print generateFeatureFile(features=[ feature ],
-                                  **values)
+        write(options.output,
+              generateFeatureFile(features=[ feature ], **values))
 
     if options.write_app:
-        print generateAppFile(artifacts=options.included_bundles,
+        write(options.output,
+              generateAppFile(artifacts=options.included_bundles,
                               apps=options.apps,
                               security=options.security,
-                              **values)
\ No newline at end of file
+                              **values))
\ No newline at end of file
diff --git a/tools/build/bazel/osgi_features.bzl b/tools/build/bazel/osgi_features.bzl
new file mode 100644
index 0000000..bc6ec4f
--- /dev/null
+++ b/tools/build/bazel/osgi_features.bzl
@@ -0,0 +1,114 @@
+"""
+ Copyright 2018-present Open Networking Foundation
+
+ 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("//tools/build/bazel:generate_workspace.bzl", "COMPILE", "TEST", "maven_coordinates")
+load("//tools/build/bazel:variables.bzl", "ONOS_VERSION", "ONOS_GROUP_ID")
+
+def dump(obj):
+  for attr in dir(obj):
+    print("obj.%s = %r" % (attr, getattr(obj, attr)))
+
+# Implementation of a rule to produce an OSGi feature XML snippet
+def _osgi_feature_impl(ctx):
+    output = ctx.outputs.feature_xml
+
+    args = [ "-O", output.path,
+             "-n", ctx.attr.name,
+             "-v", ctx.attr.version,
+             "-t", ctx.attr.description,
+            ]
+
+    inputs = []
+    for dep in ctx.attr.included_bundles:
+        args += [ "-b", maven_coordinates(dep.label) ]
+        for f in dep.java.outputs.jars:
+          inputs += [ f.class_jar ]
+
+    for dep in ctx.attr.excluded_bundles:
+        args += [ "-e", maven_coordinates(dep.label) ]
+        for f in dep.java.outputs.jars:
+          inputs += [ f.class_jar ]
+
+    for f in ctx.attr.required_features:
+      args += [ "-f", f ]
+
+    args += [ "-F" if ctx.attr.generate_file else "-E" ]
+
+    ctx.actions.run(
+        inputs = inputs,
+        outputs = [ output ],
+        arguments = args,
+        progress_message = "Generating feature %s" % ctx.attr.name,
+        executable = ctx.executable._writer
+    )
+
+osgi_feature = rule(
+    attrs = {
+        "description": attr.string(),
+        "version": attr.string(default = ONOS_VERSION),
+        "required_features": attr.string_list(default = [ "onos-api" ]),
+        "included_bundles": attr.label_list(),
+        "excluded_bundles": attr.label_list(default = []),
+        "generate_file": attr.bool(default = False),
+        "_writer": attr.label(executable=True, cfg="host", allow_files=True,
+                              default=Label("//tools/build/bazel:onos_app_writer")),
+    },
+    outputs = {
+        "feature_xml": "feature-%{name}.xml",
+    },
+    implementation = _osgi_feature_impl,
+)
+
+# OSGi feature XML header & footer constants
+FEATURES_HEADER = '''\
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+          name="onos-%s">
+    <repository>mvn:org.apache.karaf.features/standard/3.0.8/xml/features</repository>
+''' % ONOS_VERSION
+
+FEATURES_FOOTER = '</features>'
+
+# Implementation of a rule to produce an OSGi feature repo XML file
+def _osgi_feature_repo_impl(ctx):
+    output = ctx.outputs.feature_repo_xml
+
+    cmd = "(echo '%s';" % FEATURES_HEADER
+    inputs = []
+    for dep in ctx.attr.exported_features:
+        for f in dep.files.to_list():
+            inputs += [ f ]
+            cmd += "cat %s;" % f.path
+    cmd += "echo '%s') > %s;" % (FEATURES_FOOTER, output.path)
+
+    ctx.actions.run_shell(
+        inputs = inputs,
+        outputs = [ output ],
+        progress_message = "Generating feature repo %s" % ctx.attr.name,
+        command = cmd,
+    )
+
+osgi_feature_repo = rule(
+    attrs = {
+        "description": attr.string(),
+        "version": attr.string(default = ONOS_VERSION),
+        "exported_features": attr.label_list(),
+    },
+    outputs = {
+        "feature_repo_xml": "feature-repo-%{name}.xml",
+    },
+    implementation = _osgi_feature_repo_impl,
+)
diff --git a/tools/build_rules/prelude_bazel b/tools/build_rules/prelude_bazel
index 2662c07..abdd0db 100644
--- a/tools/build_rules/prelude_bazel
+++ b/tools/build_rules/prelude_bazel
@@ -3,5 +3,6 @@
         "TEST_ADAPTERS", "TEST", "TEST_REST", "METRICS", "KRYO", "NETTY", "GRPC_1_3")
 
 load("//tools/build/bazel:osgi-java-library.bzl", "osgi_jar_with_tests", "osgi_jar", "wrapped_osgi_library")
+load("//tools/build/bazel:osgi_features.bzl", "osgi_feature")
 
 generated_java_libraries()