bndtools / bnd

Bnd/Bndtools. Tooling to build OSGi bundles including Eclipse, Maven, and Gradle plugins.
https://bndtools.org
Other
532 stars 305 forks source link

`testOSGi` fails from gradle (java 9) #2432

Closed Azbesciak closed 6 years ago

Azbesciak commented 6 years ago

Hi, bellow i have placed whole stacktrace from gradle build exception. When I am running from eclipse, everything is fine. Any idea what can be wrong? As you see, tests even don't start, so code is unnecessary as I think. (Originaly tested on gradle 4.6, after fail updated to newest. Also, first tested on biz.aQute.bnd:biz.aQute.bnd.gradle:3.5.0, then on lastSuccessfulBuild) Maybe it can be problem with groovy? I don't have any specific version installed localy, but.. My Gradle config:

------------------------------------------------------------
Gradle 4.7
------------------------------------------------------------

Build time:   2018-04-18 09:09:12 UTC
Revision:     b9a962bf70638332300e7f810689cb2febbd4a6c

Groovy:       2.4.12
Ant:          Apache Ant(TM) version 1.9.9 compiled on February 2 2017
JVM:          9.0.4 (Oracle Corporation 9.0.4+11)
OS:           Windows 10 10.0 amd64

Stacktrace:

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task '<project>:testOSGi'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:103)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:73)
        at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
        at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:256)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:249)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:238)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:123)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:79)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:104)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:98)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:663)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:597)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:98)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: org.gradle.api.GradleException: <project> has errors, one error was reported
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at aQute.bnd.gradle.BndPlugin.checkProjectErrors(BndPlugin.groovy:581)
        at aQute.bnd.gradle.BndPlugin.this$2$checkProjectErrors(BndPlugin.groovy)
        at jdk.internal.reflect.GeneratedMethodAccessor278.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at aQute.bnd.gradle.BndPlugin.checkErrors(BndPlugin.groovy:564)
        at aQute.bnd.gradle.BndPlugin.this$2$checkErrors(BndPlugin.groovy)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:479)
        at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:191)
        at org.gradle.internal.metaobject.ConfigureDelegate.invokeMethod(ConfigureDelegate.java:78)
        at org.gradle.internal.metaobject.BeanDynamicObject$GroovyObjectAdapter.invokeOpaqueMethod(BeanDynamicObject.java:579)
        at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:506)
        at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:191)
        at org.gradle.internal.metaobject.ConfigureDelegate.invokeMethod(ConfigureDelegate.java:78)
        at aQute.bnd.gradle.BndPlugin$_apply_closure1_closure15_closure59.doCall(BndPlugin.groovy:310)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at org.gradle.api.internal.AbstractTask$ClosureTaskAction.execute(AbstractTask.java:726)
        at org.gradle.api.internal.AbstractTask$ClosureTaskAction.execute(AbstractTask.java:699)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:124)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:113)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:95)
        ... 30 more
bjhargrave commented 6 years ago

Is that the stacktrace you meant to include? Because that is a failure of the clean task to delete a file (probably because you use Windows and someone has a file/directory open). Nowhere in stack trace appears a Bnd type?

Azbesciak commented 6 years ago

Yes, excuse me - wrong one. I have updated with the one I intended to report.

rotty3000 commented 6 years ago

@Azbesciak could you please show the exact gradle invocation you are calling?

bjhargrave commented 6 years ago

Are you using the 3.5 version of the bnd gradle plugin? I don't think that was ever tested on Java 9. The current 4.0 snapshot builds are tested on Java 9. Please try the 4.0 snapshot. https://github.com/bndtools/bnd/tree/master/biz.aQute.bnd.gradle#using-the-latest-development-snapshot-build-of-the-bnd-gradle-plugins

bjhargrave commented 6 years ago

And besides, that exception looks like

https://github.com/bndtools/bnd/blob/bb70d103dd9c02096f516a0581be0fe9c252d581/biz.aQute.bnd.gradle/src/aQute/bnd/gradle/BndPlugin.groovy#L581

it is just failing the build because the test execution failed:

org.gradle.api.GradleException: <project> has errors, one error was reported

which would be expected unless you configure the task ignoreFailures = true.

bjhargrave commented 6 years ago

If there is a Java 9 related failure in the test execution, this is not the stacktrace to show it. Try running gradle with --debug to see if more meaningful information is emitted. Also check the TEST-*.xml file generated for the test execution.

bjhargrave commented 6 years ago

I suspect the real issue here is that the -runfw you are using is not Java 9 ready. So when the test runner installs your test bundle built under Java 9 (which has an osgi.ee requirement for Java 9) in the framework, the framework cannot resolve the Java 9 requirement.

This was a big issue in getting the Bnd build's tests to run under Java 9. Note, I also had to configure a number of --add-opens as well:

https://github.com/bndtools/bnd/blob/f36876406eb8fd4adae71c414537448a67b5ec80/build.gradle#L10-L23

(Note: The above list is larger than you may need since Bnd has groovy code to test.)

https://github.com/bndtools/bnd/blob/f36876406eb8fd4adae71c414537448a67b5ec80/build.gradle#L48-L51

Azbesciak commented 6 years ago

@rotty3000 ./gradlew clean build Recently also need to --stop it before...

@bjhargrave Yes, i tried with 4.0 (which is lastStableVersion as i think) - no difference btw, bellow fragment from my bnd.bnd from osgi test project

-runee: JavaSE-9
-runfw: org.eclipse.osgi;version='[3.13,3.14)'

actual version of org.eclipse.osgi - https://mvnrepository.com/artifact/org.eclipse.tycho/org.eclipse.osgi/3.13.0.v20171204-1916. It is a little bit strange, because in eclipse my runtime jdk is also Java 9, and it runs. Also, whole project is on java 9. And it works normally (except warnings because of guice etc...). Actually, bnd.bnd in test project has the same boot config (not buildpath etc, just flags) as normal bundles. By now I just needed to add -runvm: --add-modules java.activation and some xml-connected stuff.

I will try to add opens and make some experiments.

BTW: There is no generated file from those tests. I mean generated-gradle/test-reports is empty after build-try (just at test project, normal unit-test results are filled).

Azbesciak commented 6 years ago

So, adding those switches didn't change a lot - stack trace is a little bit different, but just because it is from plugin 4.0 (without switches it is the same - I have previously published only from 3.5 because didn't notice the difference - sorry):

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task '<project>:testOSGi'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:103)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:73)
        at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
        at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:256)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:249)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:238)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:123)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:79)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:104)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:98)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:663)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:597)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:98)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: org.gradle.api.GradleException: <project> test failure
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at aQute.bnd.gradle.TestOSGi.testWorker(TestOSGi.groovy:199)
        at aQute.bnd.gradle.TestOSGi$testWorker.callCurrent(Unknown Source)
        at aQute.bnd.gradle.TestOSGi.testOSGi(TestOSGi.groovy:162)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:46)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:788)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:755)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:124)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:113)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:95)
        ... 30 more

BTW, i have also run with --debug and --scan - nothing significantly changed (except much more log ofc).

bjhargrave commented 6 years ago

This is just the reporting of the test failure, not the test failure. So it is of no use for problem determination. The tests for the Bnd gradle plugin test the testOSGi task on Java 9. So it works for these test cases. You need to understand why your tests fail.

jest commented 6 years ago

OK, after digging more with @Azbesciak, here is more insight.

This has nothing to do with failing test. Looking at the exact error on Gradle console leads to this:

./gradlew testOSGi
Unrecognized option: --add-modules java.activation
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

> Task :uniopti.sandbox.test:testOSGi FAILED
error  : Exit code remote process 1: java -cp /home/jest/.m2/repository/org/osgi/osgi.enroute.equinox.log.adapter/2.0.0/osgi.enroute.equinox.log.adapter-2.0.0.jar:/home/jest/.m2/repository/org/eclipse/tycho/org.eclipse.osgi/3.13.0.v20171204-1916/org.eclipse.osgi-3.13.0.v20171204-1916.jar:/home/jest/work/uniopti/r/core/osgi-bnd/cnf/cache/4.0.0/bnd-cache/biz.aQute.launcher/biz.aQute.launcher-4.0.0.jar -ea --add-modules java.activation -Dlauncher.properties="/home/jest/work/uniopti/r/core/osgi-bnd/uniopti.sandbox.test/generated_gradle/launch14493758358677856949.properties" -ea aQute.launcher.Launcher

FAILURE: Build failed with an exception.

which looks like it's not Java 9 but earlier. But with --debug:

12:21:25.791 [DEBUG] [aQute.libg.command.Command] executing cmd: [java, -cp, /home/jest/.m2/repository/org/osgi/osgi.enroute.equinox.log.adapter/2.0.0/osgi.enroute.equinox.log.adapter-2.0.0.jar:/home/jest/.m2/repository/org/eclipse/tycho/org.eclipse.osgi/3.13.0.v20171204-1916/org.eclipse.osgi-3.13.0.v20171204-1916.jar:/home/jest/work/uniopti/r/core/osgi-bnd/cnf/cache/4.0.0/bnd-cache/biz.aQute.launcher/biz.aQute.launcher-4.0.0.jar, -ea, --add-modules java.activation, -Dlauncher.properties="/home/jest/work/uniopti/r/core/osgi-bnd/uniopti.sandbox.test/generated_gradle/launch12764228472176817580.properties", -ea, aQute.launcher.Launcher]

we see that --add-modules java.activation is one positional parameter to java. The reason is the wrong (?) format of our -runvm:

-runvm: \
    -ea,\
    --add-modules java.activation

Adding comma between arguments in the last line helps.

This, however leads to a different question: why Bndtools has no problem with such a construct with a space? Looks like the launcher does a different kind of magic. If I interpret it correctly, Bndtools flattens all -runvm with a space to adhere to JavaLaunchDelegate's , while bndlib keeps the parameters separated in Collection<String>.

Can anything be done (in Bndtools, I suppose) to handle this coherently?

bjhargrave commented 6 years ago

Try --add-modules=java.activation. Then there is no space.

jest commented 6 years ago

I know I can, as well as comma works, but this is unexpected to see an error when coming from (working) Bndtools to Gradle/bnd. Just user experience failure IMO.

bjhargrave commented 6 years ago

If I interpret it correctly, Bndtools flattens all -runvm with a space to adhere to JavaLaunchDelegate's , while bndlib keeps the parameters separated in Collection.

The Eclipse launcher expects the vm args to be a single string. So Bndtools has to present the -runvm information to the Eclipse launcher in the form of a single string.

I suppose Bndtools could examine each argument for embedded whitespace and wrap in quotes (assuming the Eclipse launcher can handle that). Then you would fail the same way in Bndtools and the command line. :-) If you think this should be done, please open an issue in the bndtools repo.