How can I use Eclipse's new Xtend language in my Android project?
Asked Answered
W

5

19

I'd like to write Java classes in the Xtend language (simply because its way more terse), and have it compile back down into Java classes of which I can use in my Java project. Just like coffeescript. How can I do this?

I tried creating an Xtend file just as I would do with a new class, however I get this error:

Mandatory library bundle 'org.eclipse.xtext.xbase.lib' not found on the classpath.

This disables intellisense (autocompletion). Also, even if I do get that working, how can I have it compile to a Java class?

Webster answered 6/11, 2011 at 11:27 Comment(0)
B
7

Having tried the same thing, I can confirm that enabling the Xtend Nature and adding the three Xtend libraries (mentioned earlier, 'org.eclipse.xtext.xtend2.lib', 'org.eclipse.xtext.xbase.lib' and 'com.google.inject') to the project's libraries makes the Xtend code compile, at least. However, I am also having trouble with the R class.

On closer inspection, it looks like the problem with the R class is not with it being located in a different directory. Copying the file to the main source dir with a different name doesn't change anything. Rather, it looks like the problem is with the R class being a static final class, containing several static final subclasses. If I create a simple plain-Java wrapper class that wraps a reference to R.layout.main (for example) inside a normal method, and call that from my Xtend code, then it does accept it and happily compiles.

After that, the next issue I came across was the Android compiler complaining about duplicate about.html and plugin.properties files in 'org.eclipse.xtext.xtend2.lib', 'org.eclipse.xtext.xbase.lib' and 'com.google.inject'. That is relatively easy to fix, by removing those files from two of the three .jar files. I'm not sure if it breaks anything later on, but now at least I have a working snippet of Xtend code running on the Android emulator.

Brig answered 6/11, 2011 at 22:10 Comment(0)
H
12

In Xtend inner classes are dereferenced using a dollar sign ('$') and static members are accessed using a double colon ('::').

The HelloAndroid activity code would look like this:

class XtendActivity extends Activity {

    override void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R$layout::main);
    }
}
Holsinger answered 11/11, 2011 at 12:31 Comment(6)
Facepalm thanks for that! Afterwards, it's obvious. At least it compiles now. Still, it won't start, because Android includes all the Xtext libraries while packaging, and there are duplicate files (e.g. about.html) in there (with which the Android APK builder can't deal). Since we can't build Xtend with Maven, disassembling and reconstructing those JARs (classes only) seems to be the only way, no?Dactylic
I tried to grab all sources from repos, but it turns out that you would also have to include dependencies from EMF, because XText needs those. I'll wait for a less hacky method :(Dactylic
Ahh, that explains a lot. It would've been useful if this sort of thing had been in the Xtend documentation. At least now we know. By the way, concerning those JAR files, just open them as ZIP files and remove the offending files. As far as I can see, it doesn't break anything.Brig
And you don't need EMF or Xtext jars. The only which are required are org.eclipse.xtext.xbase.lib, org.eclipse.xtext.xtend2.lib (both only contain a few classes) and google guava. Unfortunately we still have a check for google inject, which is actually not necessary (I removed that already so it's no longer there in the next release). You can make that check happy by just having a type com.google.inject.Inject somewhere on the class path.Holsinger
@NicodePoel I tried it that way, but discovered that there are indirect dependencies (not listed in the Plugins dependencies) as well, and then let it be. I will give it another try, leaving out EMF and Xtext jars as Sven suggested.Dactylic
@SvenEfftinge I have satisfied all dependencies by stripping out duplicate files from the JARs, and re-added them to the classpath (but not through plugin dependencies). Now there is an error "Mandatory library bundle 'org.eclipse.xtext.xtend2.lib' not found on the classpath." - I guess I would have to exchange Eclipse's built-in JARs with my downstripped ones and re-add those plugin dependencies?Dactylic
B
7

Having tried the same thing, I can confirm that enabling the Xtend Nature and adding the three Xtend libraries (mentioned earlier, 'org.eclipse.xtext.xtend2.lib', 'org.eclipse.xtext.xbase.lib' and 'com.google.inject') to the project's libraries makes the Xtend code compile, at least. However, I am also having trouble with the R class.

On closer inspection, it looks like the problem with the R class is not with it being located in a different directory. Copying the file to the main source dir with a different name doesn't change anything. Rather, it looks like the problem is with the R class being a static final class, containing several static final subclasses. If I create a simple plain-Java wrapper class that wraps a reference to R.layout.main (for example) inside a normal method, and call that from my Xtend code, then it does accept it and happily compiles.

After that, the next issue I came across was the Android compiler complaining about duplicate about.html and plugin.properties files in 'org.eclipse.xtext.xtend2.lib', 'org.eclipse.xtext.xbase.lib' and 'com.google.inject'. That is relatively easy to fix, by removing those files from two of the three .jar files. I'm not sure if it breaks anything later on, but now at least I have a working snippet of Xtend code running on the Android emulator.

Brig answered 6/11, 2011 at 22:10 Comment(0)
D
4

I just got it to compile, though I cannot import the "R" class, for now. Execute these steps:

First, install the Xtend SDK in the Eclipse update manager, by entering "http://download.eclipse.org/modeling/tmf/xtext/updates/composite/releases/", expanding node "TMF Xtext-2.1.0", and selecting "Xtend2 SDK". Wait, then restart Eclipse.

In your Eclipse Android project folder, edit file ".project" (e.g. by typing "nano .project" in Terminal on OSX), and change it so that it resembles this (except for the name; you can actually copy the whole content over and change the name back to your project name):

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>TestAndroidXtend</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
    <buildCommand>
        <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
        <arguments>
        </arguments>
    </buildCommand>
    <buildCommand>
        <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
        <arguments>
        </arguments>
    </buildCommand>
    <buildCommand>
        <name>org.eclipse.jdt.core.javabuilder</name>
        <arguments>
        </arguments>
    </buildCommand>
    <buildCommand>
        <name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
        <arguments>
        </arguments>
    </buildCommand>
    <buildCommand>
        <name>com.android.ide.eclipse.adt.ApkBuilder</name>
        <arguments>
        </arguments>
    </buildCommand>
</buildSpec>
<natures>
    <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
    <nature>org.eclipse.jdt.core.javanature</nature>
    <nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
</natures>

Then insert the following into file ".classpath", within the classpath element:

<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

I put it before the entry with com.android.ide.eclipse.adt.ANDROID_FRAMEWORK, perhaps you should do that too.

Back in Eclipse, refresh the project with F5, then create a folder "META-INF" at the toplevel of the project, and create an empty file "MANIFEST.MF". Paste into it these contents:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Xtend Tutorial
Bundle-SymbolicName: xtend.tutorial
Bundle-Version: 2.1.0.qualifier
Bundle-Vendor: Eclipse Modeling
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Require-Bundle: org.eclipse.xtext.xtend2.lib;bundle-version="2.0.0",
 org.junit4,
 org.aopalliance;bundle-version="1.0.0"

Perform a "Project" -> "Clean" on your project, and you can now create and use Xtend classes.

As mentioned before, I cannot import the R class, perhaps Xtend just looks in the normal "src" folder for class files.

Dactylic answered 6/11, 2011 at 13:50 Comment(2)
you replaced the bundle with xtend's bundle, i think thats why it stopped producing the R file.Webster
it did not stop producing the file, i could not use it in the .xtend class - if you ask a question, at least TRY the answers before making guesses.Dactylic
H
1

I haven't done any Android development so far. But Xtend code is compiled against a thin runtime library (written in Java). It's mainly just three jars: - org.eclipse.xtext.xtend2.lib - org.eclipse.xtext.xbase.lib - com.google.collect

They are all installed in eclipse, so you should find them in the plugins folder of your installation. We usually use OSGI and PDE to have them on the classpath, but it's really just a classpath dependency.

Xtend can import everything which is on the classpath just like Java. It reuses the Java project configuration and also the Java Development Tools. So as long as you have the "R" class on the class path it should work. Please file a bugzilla at bugs.eclipse.org (under Modeling/TMF/Xtext) If you can't reference it although you can from a Java which sits next to the Xtend file. And provide a small example and some additional explanation.

Holsinger answered 6/11, 2011 at 19:8 Comment(2)
I think you have a clear idea how I might be able to achieve this, but can you provide a better concise answer (step-by-step) as to how I can achieve this in any Java project?Webster
@sven-efftinge: it's not that easy, since this seems to have something to do with the R class being generated on the fly. an easy example is not possible, because you need the ADT tools installed in eclipse for it to work.Dactylic
T
0

Here's another gotcha: Make sure your parameter names don't conflict with anything in R.java.

I created a (Java) Activity using the wizard, copied the code, deleted the Activity, and created a MainActivity.xtend in its place, like so:


public class MainActivity extends Activity {

    override void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R$layout::activity_main);
    }

    override boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R$menu::activity_main, menu);
        return true;
    }

It looks fine in the .xtend file, but it doesn't build because the generated Java code

  public boolean onCreateOptionsMenu(final Menu menu) {
    MenuInflater _menuInflater = this.getMenuInflater();
    _menuInflater.inflate(menu.activity_main, menu);
    return true;
  }

doesn't like activity_main at this point.

It took me a few minutes to figure out that the "menu" parameter was overriding R.menu.activity_main. Once I changed the .xtend's "menu" parameter to "optionsMenu", it worked fine.

Townes answered 16/10, 2012 at 2:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.