Bug 853045 - Part 3: Add AndroidEclipseBackend. r=gps
test_recursive_make.py depends on the test data added by this commit, so it's here rather than in the earlier commit.
This commit is contained in:
190
python/mozbuild/mozbuild/backend/android_eclipse.py
Normal file
190
python/mozbuild/mozbuild/backend/android_eclipse.py
Normal file
@@ -0,0 +1,190 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from mozpack.copier import FileCopier
|
||||
from mozpack.files import (FileFinder, PreprocessedFile)
|
||||
from mozpack.manifests import InstallManifest
|
||||
import mozpack.path as mozpath
|
||||
|
||||
from .common import CommonBackend
|
||||
from ..frontend.data import (
|
||||
AndroidEclipseProjectData,
|
||||
SandboxDerived,
|
||||
SandboxWrapped,
|
||||
)
|
||||
from ..makeutil import Makefile
|
||||
from ..util import ensureParentDir
|
||||
|
||||
|
||||
class AndroidEclipseBackend(CommonBackend):
|
||||
"""Backend that generates Android Eclipse project files.
|
||||
"""
|
||||
|
||||
def _init(self):
|
||||
CommonBackend._init(self)
|
||||
|
||||
|
||||
def consume_object(self, obj):
|
||||
"""Write out Android Eclipse project files."""
|
||||
|
||||
if not isinstance(obj, SandboxDerived):
|
||||
return
|
||||
|
||||
CommonBackend.consume_object(self, obj)
|
||||
|
||||
# If CommonBackend acknowledged the object, we're done with it.
|
||||
if obj._ack:
|
||||
return
|
||||
|
||||
# We don't want to handle most things, so we just acknowledge all objects...
|
||||
obj.ack()
|
||||
|
||||
# ... and handle the one case we care about specially.
|
||||
if isinstance(obj, SandboxWrapped) and isinstance(obj.wrapped, AndroidEclipseProjectData):
|
||||
self._process_android_eclipse_project_data(obj.wrapped, obj.srcdir, obj.objdir)
|
||||
|
||||
def consume_finished(self):
|
||||
"""The common backend handles WebIDL and test files. We don't handle
|
||||
these, so we don't call our superclass.
|
||||
"""
|
||||
|
||||
def _Element_for_classpathentry(self, cpe):
|
||||
"""Turn a ClassPathEntry into an XML Element, like one of:
|
||||
<classpathentry including="**/*.java" kind="src" path="preprocessed"/>
|
||||
<classpathentry including="**/*.java" excluding="org/mozilla/gecko/Excluded.java|org/mozilla/gecko/SecondExcluded.java" kind="src" path="src"/>
|
||||
<classpathentry including="**/*.java" kind="src" path="thirdparty">
|
||||
<attributes>
|
||||
<attribute name="ignore_optional_problems" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
"""
|
||||
e = ET.Element('classpathentry')
|
||||
e.set('kind', 'src')
|
||||
e.set('including', '**/*.java')
|
||||
e.set('path', cpe.path)
|
||||
if cpe.exclude_patterns:
|
||||
e.set('excluding', '|'.join(sorted(cpe.exclude_patterns)))
|
||||
if cpe.ignore_warnings:
|
||||
attrs = ET.SubElement(e, 'attributes')
|
||||
attr = ET.SubElement(attrs, 'attribute')
|
||||
attr.set('name', 'ignore_optional_problems')
|
||||
attr.set('value', 'true')
|
||||
return e
|
||||
|
||||
def _Element_for_referenced_project(self, name):
|
||||
"""Turn a referenced project name into an XML Element, like:
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/Fennec"/>
|
||||
"""
|
||||
e = ET.Element('classpathentry')
|
||||
e.set('kind', 'src')
|
||||
e.set('combineaccessrules', 'false')
|
||||
# All project directories are in the same root; this
|
||||
# reference is absolute in the Eclipse namespace.
|
||||
e.set('path', '/' + name)
|
||||
return e
|
||||
|
||||
def _Element_for_extra_jar(self, name):
|
||||
"""Turn a referenced JAR name into an XML Element, like:
|
||||
<classpathentry kind="lib" path="libs/robotium-solo-4.3.1.jar"/>
|
||||
"""
|
||||
e = ET.Element('classpathentry')
|
||||
e.set('kind', 'lib')
|
||||
# All project directories are in the same root; this
|
||||
# reference is absolute in the Eclipse namespace.
|
||||
e.set('path', 'libs/' + name)
|
||||
return e
|
||||
|
||||
def _manifest_for_project(self, srcdir, project):
|
||||
manifest = InstallManifest()
|
||||
|
||||
if project.manifest:
|
||||
manifest.add_copy(mozpath.join(srcdir, project.manifest), 'AndroidManifest.xml')
|
||||
|
||||
if project.res:
|
||||
manifest.add_symlink(mozpath.join(srcdir, project.res), 'res')
|
||||
|
||||
if project.assets:
|
||||
manifest.add_symlink(mozpath.join(srcdir, project.assets), 'assets')
|
||||
|
||||
for cpe in project._classpathentries:
|
||||
manifest.add_symlink(mozpath.join(srcdir, cpe.srcdir), cpe.dstdir)
|
||||
|
||||
# JARs and native libraries go in the same place. This
|
||||
# wouldn't be a problem, except we only know the contents of
|
||||
# (a subdirectory of) libs/ after a successful build and
|
||||
# package, which is after build-backend time. So we use a
|
||||
# pattern symlink that is resolved at manifest install time.
|
||||
if project.libs:
|
||||
manifest.add_pattern_copy(mozpath.join(srcdir, project.libs), '**', 'libs')
|
||||
|
||||
for extra_jar in sorted(project.extra_jars):
|
||||
manifest.add_copy(mozpath.join(srcdir, extra_jar), mozpath.join('libs', os.path.basename(extra_jar)))
|
||||
|
||||
return manifest
|
||||
|
||||
def _process_android_eclipse_project_data(self, data, srcdir, objdir):
|
||||
# This can't be relative to the environment's topsrcdir,
|
||||
# because during testing topsrcdir is faked.
|
||||
template_directory = os.path.abspath(mozpath.join(os.path.dirname(__file__),
|
||||
'templates', 'android_eclipse'))
|
||||
|
||||
project_directory = mozpath.join(self.environment.topobjdir, 'android_eclipse', data.name)
|
||||
manifest_path = mozpath.join(self.environment.topobjdir, 'android_eclipse', '%s.manifest' % data.name)
|
||||
|
||||
manifest = self._manifest_for_project(srcdir, data)
|
||||
ensureParentDir(manifest_path)
|
||||
manifest.write(path=manifest_path)
|
||||
|
||||
classpathentries = []
|
||||
for cpe in sorted(data._classpathentries, key=lambda x: x.path):
|
||||
e = self._Element_for_classpathentry(cpe)
|
||||
classpathentries.append(ET.tostring(e))
|
||||
|
||||
for name in sorted(data.referenced_projects):
|
||||
e = self._Element_for_referenced_project(name)
|
||||
classpathentries.append(ET.tostring(e))
|
||||
|
||||
for name in sorted(data.extra_jars):
|
||||
e = self._Element_for_extra_jar(os.path.basename(name))
|
||||
classpathentries.append(ET.tostring(e))
|
||||
|
||||
defines = {}
|
||||
defines['IDE_OBJDIR'] = objdir
|
||||
defines['IDE_TOPOBJDIR'] = self.environment.topobjdir
|
||||
defines['IDE_SRCDIR'] = srcdir
|
||||
defines['IDE_TOPSRCDIR'] = self.environment.topsrcdir
|
||||
defines['IDE_PROJECT_NAME'] = data.name
|
||||
defines['IDE_PACKAGE_NAME'] = data.package_name
|
||||
defines['IDE_PROJECT_DIRECTORY'] = project_directory
|
||||
defines['IDE_RELSRCDIR'] = mozpath.relpath(srcdir, self.environment.topsrcdir)
|
||||
defines['IDE_CLASSPATH_ENTRIES'] = '\n'.join('\t' + cpe for cpe in classpathentries)
|
||||
defines['IDE_RECURSIVE_MAKE_TARGETS'] = ' '.join(sorted(data.recursive_make_targets))
|
||||
# Like android.library=true
|
||||
defines['IDE_PROJECT_LIBRARY_SETTING'] = 'android.library=true' if data.is_library else ''
|
||||
# Like android.library.reference.1=FennecBrandingResources
|
||||
defines['IDE_PROJECT_LIBRARY_REFERENCES'] = '\n'.join(
|
||||
'android.library.reference.%s=%s' % (i + 1, ref)
|
||||
for i, ref in enumerate(sorted(data.included_projects)))
|
||||
|
||||
copier = FileCopier()
|
||||
finder = FileFinder(template_directory)
|
||||
for input_filename, f in itertools.chain(finder.find('**'), finder.find('.**')):
|
||||
if input_filename == 'AndroidManifest.xml' and not data.is_library:
|
||||
# Main projects supply their own manifests.
|
||||
continue
|
||||
copier.add(input_filename, PreprocessedFile(
|
||||
mozpath.join(finder.base, input_filename),
|
||||
depfile_path=None,
|
||||
marker='#',
|
||||
defines=defines,
|
||||
extra_depends={mozpath.join(finder.base, input_filename)}))
|
||||
|
||||
# When we re-create the build backend, we kill everything that was there.
|
||||
copier.copy(project_directory, skip_if_older=False, remove_unaccounted=True)
|
||||
@@ -0,0 +1 @@
|
||||
<string name="label">library1</string>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- Placeholder. -->
|
||||
@@ -0,0 +1 @@
|
||||
<!-- Placeholder. -->
|
||||
@@ -0,0 +1 @@
|
||||
# Placeholder.
|
||||
@@ -0,0 +1 @@
|
||||
# Placeholder.
|
||||
@@ -0,0 +1 @@
|
||||
<string name="label">main1</string>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- Placeholder. -->
|
||||
@@ -0,0 +1 @@
|
||||
package a.a;
|
||||
@@ -0,0 +1 @@
|
||||
package b;
|
||||
@@ -0,0 +1 @@
|
||||
package d.e;
|
||||
@@ -0,0 +1 @@
|
||||
<!-- Placeholder. -->
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
|
||||
p = add_android_eclipse_library_project('library1')
|
||||
p.package_name = 'org.mozilla.test.library1'
|
||||
p.res = 'library1/resources'
|
||||
|
||||
p = add_android_eclipse_library_project('library2')
|
||||
p.package_name = 'org.mozilla.test.library2'
|
||||
|
||||
p = add_android_eclipse_project('main1', 'main1/AndroidManifest.xml')
|
||||
p.package_name = 'org.mozilla.test.main1'
|
||||
p.recursive_make_targets += ['target1', 'target2']
|
||||
|
||||
p = add_android_eclipse_project('main2', 'main2/AndroidManifest.xml')
|
||||
p.package_name = 'org.mozilla.test.main2'
|
||||
p.res = 'main2/res'
|
||||
p.assets = 'main2/assets'
|
||||
p.extra_jars = ['main2/extra.jar']
|
||||
|
||||
p = add_android_eclipse_project('main3', 'main3/AndroidManifest.xml')
|
||||
p.package_name = 'org.mozilla.test.main3'
|
||||
cpe = p.add_classpathentry('a', 'main3/a', dstdir='a/a')
|
||||
cpe = p.add_classpathentry('b', 'main3/b', dstdir='b')
|
||||
cpe.exclude_patterns += ['b/Excludes.java', 'b/Excludes2.java']
|
||||
cpe = p.add_classpathentry('c', 'main3/c', dstdir='d/e')
|
||||
cpe.ignore_warnings = True
|
||||
|
||||
p = add_android_eclipse_project('main4', 'main3/AndroidManifest.xml')
|
||||
p.package_name = 'org.mozilla.test.main3'
|
||||
p.referenced_projects += ['library1']
|
||||
p.included_projects += ['library2']
|
||||
p.recursive_make_targets += ['target3', 'target4']
|
||||
|
||||
DIRS += ['subdir']
|
||||
@@ -0,0 +1,13 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
DEFINES['FOO'] = 'FOO'
|
||||
|
||||
p = add_android_eclipse_library_project('sublibrary')
|
||||
p.package_name = 'org.mozilla.test.sublibrary'
|
||||
p.is_library = True
|
||||
|
||||
p = add_android_eclipse_project('submain', 'submain/AndroidManifest.xml')
|
||||
p.package_name = 'org.mozilla.test.submain'
|
||||
p.recursive_make_targets += ['subtarget1', 'subtarget2']
|
||||
@@ -0,0 +1 @@
|
||||
<!-- Placeholder. -->
|
||||
151
python/mozbuild/mozbuild/test/backend/test_android_eclipse.py
Normal file
151
python/mozbuild/mozbuild/test/backend/test_android_eclipse.py
Normal file
@@ -0,0 +1,151 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from mozbuild.backend.android_eclipse import AndroidEclipseBackend
|
||||
from mozbuild.frontend.emitter import TreeMetadataEmitter
|
||||
from mozbuild.frontend.reader import BuildReader
|
||||
from mozbuild.test.backend.common import BackendTester
|
||||
from mozpack.manifests import InstallManifest
|
||||
from mozunit import main
|
||||
|
||||
import mozpack.path as mozpath
|
||||
|
||||
class TestAndroidEclipseBackend(BackendTester):
|
||||
def __init__(self, *args, **kwargs):
|
||||
BackendTester.__init__(self, *args, **kwargs)
|
||||
self.env = None
|
||||
|
||||
def assertExists(self, *args):
|
||||
p = mozpath.join(self.env.topobjdir, 'android_eclipse', *args)
|
||||
self.assertTrue(os.path.exists(p), "Path %s exists" % p)
|
||||
|
||||
def assertNotExists(self, *args):
|
||||
p = mozpath.join(self.env.topobjdir, 'android_eclipse', *args)
|
||||
self.assertFalse(os.path.exists(p), "Path %s does not exist" % p)
|
||||
|
||||
def test_library_project_files(self):
|
||||
"""Ensure we generate reasonable files for library projects."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
for f in ['.classpath',
|
||||
'.project',
|
||||
'.settings',
|
||||
'AndroidManifest.xml',
|
||||
'project.properties']:
|
||||
self.assertExists('library1', f)
|
||||
|
||||
def test_main_project_files(self):
|
||||
"""Ensure we generate reasonable files for main (non-library) projects."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
for f in ['.classpath',
|
||||
'.externalToolBuilders',
|
||||
'.project',
|
||||
'.settings',
|
||||
'build.xml',
|
||||
'gen',
|
||||
'lint.xml',
|
||||
'project.properties']:
|
||||
self.assertExists('main1', f)
|
||||
|
||||
def test_library_manifest(self):
|
||||
"""Ensure we generate manifest for library projects."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertExists('library1', 'AndroidManifest.xml')
|
||||
|
||||
def test_classpathentries(self):
|
||||
"""Ensure we produce reasonable classpathentries."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertExists('main3', '.classpath')
|
||||
# This is brittle but simple.
|
||||
with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main3', '.classpath'), 'rt') as fh:
|
||||
lines = fh.readlines()
|
||||
lines = [line.strip() for line in lines]
|
||||
self.assertIn('<classpathentry including="**/*.java" kind="src" path="a" />', lines)
|
||||
self.assertIn('<classpathentry excluding="b/Excludes.java|b/Excludes2.java" including="**/*.java" kind="src" path="b" />', lines)
|
||||
self.assertIn('<classpathentry including="**/*.java" kind="src" path="c"><attributes><attribute name="ignore_optional_problems" value="true" /></attributes></classpathentry>', lines)
|
||||
|
||||
def test_library_project_setting(self):
|
||||
"""Ensure we declare a library project correctly."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
|
||||
self.assertExists('library1', 'project.properties')
|
||||
with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'library1', 'project.properties'), 'rt') as fh:
|
||||
lines = fh.readlines()
|
||||
lines = [line.strip() for line in lines]
|
||||
self.assertIn('android.library=true', lines)
|
||||
|
||||
self.assertExists('main1', 'project.properties')
|
||||
with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main1', 'project.properties'), 'rt') as fh:
|
||||
lines = fh.readlines()
|
||||
lines = [line.strip() for line in lines]
|
||||
self.assertNotIn('android.library=true', lines)
|
||||
|
||||
def test_referenced_projects(self):
|
||||
"""Ensure we reference another project correctly."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertExists('main4', '.classpath')
|
||||
# This is brittle but simple.
|
||||
with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main4', '.classpath'), 'rt') as fh:
|
||||
lines = fh.readlines()
|
||||
lines = [line.strip() for line in lines]
|
||||
self.assertIn('<classpathentry combineaccessrules="false" kind="src" path="/library1" />', lines)
|
||||
|
||||
def test_included_projects(self):
|
||||
"""Ensure we include another project correctly."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertExists('main4', 'project.properties')
|
||||
# This is brittle but simple.
|
||||
with open(mozpath.join(self.env.topobjdir, 'android_eclipse', 'main4', 'project.properties'), 'rt') as fh:
|
||||
lines = fh.readlines()
|
||||
lines = [line.strip() for line in lines]
|
||||
self.assertIn('android.library.reference.1=library2', lines)
|
||||
|
||||
def assertInManifest(self, project_name, *args):
|
||||
manifest_path = mozpath.join(self.env.topobjdir, 'android_eclipse', '%s.manifest' % project_name)
|
||||
manifest = InstallManifest(manifest_path)
|
||||
for arg in args:
|
||||
self.assertIn(arg, manifest, '%s in manifest for project %s' % (arg, project_name))
|
||||
|
||||
def assertNotInManifest(self, project_name, *args):
|
||||
manifest_path = mozpath.join(self.env.topobjdir, 'android_eclipse', '%s.manifest' % project_name)
|
||||
manifest = InstallManifest(manifest_path)
|
||||
for arg in args:
|
||||
self.assertNotIn(arg, manifest, '%s not in manifest for project %s' % (arg, project_name))
|
||||
|
||||
def test_manifest_main_manifest(self):
|
||||
"""Ensure we symlink manifest if asked to for main projects."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertInManifest('main1', 'AndroidManifest.xml')
|
||||
|
||||
def test_manifest_res(self):
|
||||
"""Ensure we symlink res/ iff asked to."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertInManifest('library1', 'res')
|
||||
self.assertNotInManifest('library2', 'res')
|
||||
|
||||
def test_manifest_classpathentries(self):
|
||||
"""Ensure we symlink classpathentries correctly."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertInManifest('main3', 'a/a', 'b', 'd/e')
|
||||
|
||||
def test_manifest_assets(self):
|
||||
"""Ensure we symlink assets/ iff asked to."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertNotInManifest('main1', 'assets')
|
||||
self.assertInManifest('main2', 'assets')
|
||||
|
||||
def test_manifest_extra_jars(self):
|
||||
"""Ensure we symlink extra jars iff asked to."""
|
||||
self.env = self._consume('android_eclipse', AndroidEclipseBackend)
|
||||
self.assertNotInManifest('main1', 'libs')
|
||||
self.assertInManifest('main2', 'libs/extra.jar')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -616,6 +616,39 @@ class TestRecursiveMakeBackend(BackendTester):
|
||||
|
||||
self.assertIn('JAR_MANIFEST := %s/jar.mn' % env.topsrcdir, lines)
|
||||
|
||||
def test_android_eclipse(self):
|
||||
env = self._consume('android_eclipse', RecursiveMakeBackend)
|
||||
|
||||
with open(mozpath.join(env.topobjdir, 'backend.mk'), 'rb') as fh:
|
||||
lines = fh.readlines()
|
||||
|
||||
lines = [line.rstrip() for line in lines]
|
||||
|
||||
# Dependencies first.
|
||||
self.assertIn('ANDROID_ECLIPSE_PROJECT_main1: target1 target2', lines)
|
||||
self.assertIn('ANDROID_ECLIPSE_PROJECT_main4: target3 target4', lines)
|
||||
|
||||
command_template = '\t$(call py_action,process_install_manifest,' + \
|
||||
'--no-remove --no-remove-all-directory-symlinks ' + \
|
||||
'--no-remove-empty-directories %s %s.manifest)'
|
||||
# Commands second.
|
||||
for project_name in ['main1', 'main2', 'library1', 'library2']:
|
||||
stem = '%s/android_eclipse/%s' % (env.topobjdir, project_name)
|
||||
self.assertIn(command_template % (stem, stem), lines)
|
||||
|
||||
# Projects declared in subdirectories.
|
||||
with open(mozpath.join(env.topobjdir, 'subdir', 'backend.mk'), 'rb') as fh:
|
||||
lines = fh.readlines()
|
||||
|
||||
lines = [line.rstrip() for line in lines]
|
||||
|
||||
self.assertIn('ANDROID_ECLIPSE_PROJECT_submain: subtarget1 subtarget2', lines)
|
||||
|
||||
for project_name in ['submain', 'sublibrary']:
|
||||
# Destination and install manifest are relative to topobjdir.
|
||||
stem = '%s/android_eclipse/%s' % (env.topobjdir, project_name)
|
||||
self.assertIn(command_template % (stem, stem), lines)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user