Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » [Xcore][gradle] Two mysterious problems.
[Xcore][gradle] Two mysterious problems. [message #1754613] Tue, 21 February 2017 04:55 Go to next message
Nicolas Rouquette is currently offline Nicolas RouquetteFriend
Messages: 38
Registered: December 2014
Member
I am using gradle to build a DSL from an Xcore metamodel here:
https://github.com/jpl-imce/oml.core

This project is organized in a similar way than the itemis example here:
https://github.com/itemis/itemis-blog/tree/xcore_gradle

There are two differences though:

1) The behavior of the XcoreGenerator is different

In the itemis example, the XcoreGenerator does not produce any *.ecore metamodel per se. You can see this in the generated code for the metamodel package initialization:

/org.xtext.example.mydsl.model/build/xcore/main/org/xtext/example/mydsl/impl/MydslPackageImpl.java

public class MydslPackageImpl extends EPackageImpl implements MydslPackage {
	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	private EClass modelEClass = null;

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	private EClass greetingEClass = null;

	/**
	 * Creates an instance of the model <b>Package</b>, registered with
	 * {@link org.eclipse.emf.ecore.EPackage.Registry EPackage.Registry} by the package
	 * package URI value.
	 * <p>Note: the correct way to create the package is via the static
	 * factory method {@link #init init()}, which also performs
	 * initialization of the package, or returns the registered package,
	 * if one already exists.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see org.eclipse.emf.ecore.EPackage.Registry
	 * @see org.xtext.example.mydsl.MydslPackage#eNS_URI
	 * @see #init()
	 * @generated
	 */
	private MydslPackageImpl() {
		super(eNS_URI, MydslFactory.eINSTANCE);
	}
...
public void initializePackageContents() {
		if (isInitialized) return;
		isInitialized = true;

		// Initialize package
		setName(eNAME);
		setNsPrefix(eNS_PREFIX);
		setNsURI(eNS_URI);

		// Obtain other dependent packages
		EcorePackage theEcorePackage = (EcorePackage)EPackage.Registry.INSTANCE.getEPackage(EcorePackage.eNS_URI);

		// Create type parameters

		// Set bounds for type parameters

		// Add supertypes to classes

		// Initialize classes, features, and operations; add parameters
		initEClass(modelEClass, Model.class, "Model", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
		initEReference(getModel_Greetings(), this.getGreeting(), null, "greetings", null, 0, -1, Model.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, IS_COMPOSITE, !IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);

		initEClass(greetingEClass, Greeting.class, "Greeting", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
		initEAttribute(getGreeting_Name(), theEcorePackage.getEString(), "name", null, 0, 1, Greeting.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, !IS_UNIQUE, !IS_DERIVED, IS_ORDERED);

		// Create resource
		createResource(eNS_URI);
	}
...


In my case, the XcoreGenerator produces an *.ecore file;
however, it fails to save it because it is running outside of Eclipse
(i.e., there is no EMF ResourceSet pathmap nor any workspaceRoot
to map the generated resource to an external file location).

It's clear from the generated package impl code that there *should*
be an *.ecore file saved though:

public class OmlPackageImpl extends EPackageImpl implements OmlPackage {
	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	protected String packageFilename = "oml.ecore";

...
public static OmlPackage init() {
		if (isInited) return (OmlPackage)EPackage.Registry.INSTANCE.getEPackage(OmlPackage.eNS_URI);

		// Obtain or create and register package
		OmlPackageImpl theOmlPackage = (OmlPackageImpl)(EPackage.Registry.INSTANCE.get(eNS_URI) instanceof OmlPackageImpl ? EPackage.Registry.INSTANCE.get(eNS_URI) : new OmlPackageImpl());

		isInited = true;

		// Initialize simple dependencies
		EcorePackage.eINSTANCE.eClass();

		// Load packages
		theOmlPackage.loadPackage();

		// Fix loaded packages
		theOmlPackage.fixPackageContents();

		// Mark meta-data to indicate it can't be changed
		theOmlPackage.freeze();

  
		// Update the registry and return the package
		EPackage.Registry.INSTANCE.put(OmlPackage.eNS_URI, theOmlPackage);
		return theOmlPackage;
	}
...


Questions:

1) What determines whether the XcoreGenerator produces code that requires an *.ecore metamodel for initializing the metamodel package or not?

I'm not sure if this difference is attributable to the XcoreGenerator per se or to behavior it inherits from the EMF code generator.

I tried to comment out some parts of my DSL hoping to get a smaller reproducible example; well, it's not obvious.

2) Assuming that I won't be so lucky to define a DSL without needing an *.ecore file,
how could I configure the gradle script such that the EMF ResourceSet used by the XcoreGenerator would have a pathmap like:

"oml.model" = file("${rootDir}/oml.model")

Is there some configuration that could be set in the xtext section?


xtext {
	version = "${xtextVersion}"
	languages {
		ecore {
			setup = 'org.eclipse.xtext.ecore.EcoreSupport'
		}
		codegen {
			setup = 'org.eclipse.emf.codegen.ecore.xtext.GenModelSupport'
		}
		xcore {
			setup = 'org.eclipse.emf.ecore.xcore.XcoreStandaloneSetup'
			generator {
                javaSourceLevel = '1.8'
                outlet {
                    producesJava = true
                }
            }
		}
	}
}


If not, I wonder if it would be possible to define locally in the project a custom
XcoreStandaloneSetup to configure the ResourceSet's pathmap.

I'm not an expert in gradle so I would appreciate very much some guidance on this.
Re: [Xcore][gradle] Two mysterious problems. [message #1754620 is a reply to message #1754613] Tue, 21 February 2017 06:47 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 29112
Registered: July 2009
Senior Member
If the package class is "too large" the generated package initialization code will exceed Java's 64k byte code limit. So the GenModel initialization code tries to estimate what too large might be in terms of the size of the Ecore model and initializes the GenPackage's loadInitialization feature accordingly. But you can add @GenModel(loadInitialization="false") before the package declaration in your *.xcore and see if in fact the generated code is too large or not. If that's okay, I suppose that solves your problem. If it is too large, you could modify the itemis example to add @GenModel(loadInitialization="true") to see if that example works or not. If not, you could ask for their help (perhaps better on the Xtext forum) for how to make that work for their example.

That being said, I suspect that the general issue is perhaps the way that org.eclipse.emf.codegen.ecore.genmodel.generator.GenPackageGeneratorAdapter.generatePackageSerialization(GenPackage, Monitor) is implemented. It creates its own resource set in which to create the Ecore resource and then it calls save on the resource. So it does not call org.eclipse.emf.codegen.ecore.generator.AbstractGeneratorAdapter.createOutputStream(URI) and hence bypasses org.eclipse.emf.ecore.xcore.generator.XcoreGenModelGeneratorAdapterFactory.createOutputStream(URI) and maybe that's important for this case.

But note that if my help is required, I need a way to reproduce the problem that does not involve manually setting up a complex test case (i.e., that does not involve spending more time to reproduce the problem than to solve it).
Re: [Xcore][gradle] Two mysterious problems. [message #1754688 is a reply to message #1754620] Tue, 21 February 2017 14:49 Go to previous messageGo to next message
Nicolas Rouquette is currently offline Nicolas RouquetteFriend
Messages: 38
Registered: December 2014
Member
Thanks Ed for your explanation!

Your suspicion seems right to me: I was wondering why the XcoreGenerator can save the generated *.java but not *.ecore.

I followed your suggestion to add @GenModel(loadInitialization="true") to the itemis example and indeed it fails with the same error/stack trace.

git clone git@github.com:itemis/itemis-blog.git
cd itemis-blog
git checkout xcore_gradle
cd org.xtext.example.mydsl.parent/
./gradlew :org.xtext.example.mydsl.model:generateXtext


This works.

Edit the mydsl.xcore like this:

@Ecore(nsPrefix="mydsl",nsURI="http://www.xtext.org/example/mydsl/MyDsl")
@GenModel(
    bundleManifest="false",
    modelDirectory="org.xtext.example.mydsl.model/build/xcore/main", 
    complianceLevel="8.0"
)
@GenModel(loadInitialization="true") 
 
package org.xtext.example.mydsl

class Model {
	contains Greeting[] greetings
}

class Greeting {
	String name
}


Now, I get the same error/stacktrace than my larger example:

./gradlew :org.xtext.example.mydsl.model:generateXtext
:org.xtext.example.mydsl.model:generateXtext
java.io.IOException: The path '/org.xtext.example.mydsl.model/build/xcore/main/org/xtext/example/mydsl/impl/mydsl.ecore' is unmapped
        at org.eclipse.emf.ecore.resource.impl.PlatformResourceURIHandlerImpl.createOutputStream(PlatformResourceURIHandlerImpl.java:501)
        at org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl.createOutputStream(ExtensibleURIConverterImpl.java:349)
        at org.eclipse.emf.ecore.resource.impl.ResourceImpl.saveOnlyIfChangedWithMemoryBuffer(ResourceImpl.java:1234)
        at org.eclipse.emf.ecore.resource.impl.ResourceImpl.save(ResourceImpl.java:985)
        at org.eclipse.emf.codegen.ecore.genmodel.generator.GenPackageGeneratorAdapter.generatePackageSerialization(GenPackageGeneratorAdapter.java:544)
        at org.eclipse.emf.codegen.ecore.genmodel.generator.GenPackageGeneratorAdapter.generateModel(GenPackageGeneratorAdapter.java:213)
        at org.eclipse.emf.codegen.ecore.genmodel.generator.GenBaseGeneratorAdapter.doGenerate(GenBaseGeneratorAdapter.java:215)
        at org.eclipse.emf.codegen.ecore.generator.AbstractGeneratorAdapter.generate(AbstractGeneratorAdapter.java:317)
        at org.eclipse.emf.codegen.ecore.generator.Generator.generate(Generator.java:708)
        at org.eclipse.emf.codegen.ecore.generator.Generator.generate(Generator.java:619)
        at org.eclipse.emf.ecore.xcore.generator.XcoreGenerator.generateGenModel(XcoreGenerator.java:280)
        at org.eclipse.emf.ecore.xcore.generator.XcoreGenerator.doGenerate(XcoreGenerator.java:228)
        at org.eclipse.xtext.generator.GeneratorDelegate.doGenerate(GeneratorDelegate.java:45)
        at org.eclipse.xtext.generator.GeneratorDelegate.generate(GeneratorDelegate.java:34)
        at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder.generate(IncrementalBuilder.java:350)
        at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder$5.apply(IncrementalBuilder.java:259)
...


Regarding org.eclipse.emf.codegen.ecore.genmodel.generator.GenPackageGeneratorAdapter.generatePackageSerialization(GenPackage, Monitor), I wonder if it would be sufficient in this case
to copy the adapters from the original resource set; something like this:

 final ResourceSet originalSet = genModel.eResource().getResourceSet();
        EPackage originalPackage = genPackage.getEcorePackage();

        ResourceSet outputSet = new ResourceSetImpl();
        outputSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new EcoreResourceFactoryImpl());
        outputSet.eAdapters().addAll(originalSet.eAdapters());
 


If only it were that easy to try this on this example...
Re: [Xcore][gradle] Two mysterious problems. [message #1754697 is a reply to message #1754688] Tue, 21 February 2017 16:08 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 29112
Registered: July 2009
Senior Member
Setting @GenModel(loadInitialization="false") doesn't work for your example?

And no, adding adapters to the output resource set won't change how it creates the output stream for saving the Ecore resource.
Re: [Xcore][gradle] Two mysterious problems. [message #1754714 is a reply to message #1754697] Tue, 21 February 2017 18:19 Go to previous message
Nicolas Rouquette is currently offline Nicolas RouquetteFriend
Messages: 38
Registered: December 2014
Member
@GenModel(loadInitialization="false") works for my example -- i.e., I can generate & compile the generated code without errors.
For now, that's a good enough workaround. Thanks for looking into this!
Previous Topic:get name/address of model
Next Topic:MagicDraw reading UML Profiles
Goto Forum:
  


Current Time: Tue Jan 16 23:44:50 GMT 2018

Powered by FUDForum. Page generated in 0.02223 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software