Android-runtime: Static binding generator fails when using Webpack + Workers

Created on 8 Jun 2017  ยท  45Comments  ยท  Source: NativeScript/android-runtime

Example case:
Setup - project with web worker file where the worker imports 'globals'.
If you webpack the project, a bundle named [hash].worker.js will be generated and it will contain the whole tns-core-modules package.
Since tns-core-modules is also contained in the vendor chunk, the static binding generator will fail:

Warning: there already is an extend called com.tns.FragmentClass.
Warning: The static binding generator will generate extend from:b8fc197268e0dd59663c.worker.js implementation
:asbg:generateBindings
Exception in thread "main" java.io.IOException: File already exists. This may lead to undesired behavior. Building 55% > :asbg:generateBindings
Please change the name of one of the extended classes.
/home/[email protected]/nativescript-webpack-workers-issue/platforms/android/src/main/java/com/tns/FragmentClass.java
        at org.nativescript.staticbindinggenerator.Generator.writeBindings(Generator.java:59)
        at org.nativescript.staticbindinggenerator.Main.main(Main.java:15)
:asbg:generateBindings FAILED
bug

Most helpful comment

Hi,

I believe I am having the same problem as @sis0k0 , trying to webpack an {N} app with a web worker:

Exception in thread "main" java.io.IOException: File already exists. This may lead to undesired behavior.
Please change the name of one of the extended classes.
File:/home/quentin/Projects/hubup-nativescript-clover/platforms/android/src/main/java/com/tns/FragmentClass.java Class: com.tns.FragmentClass
        at org.nativescript.staticbindinggenerator.Generator.writeBindings(Generator.java:68)
        at org.nativescript.staticbindinggenerator.Main.main(Main.java:15)
:asbg:generateBindings FAILED

I waited a few weeks before migrating to {N} V3, but now, it is becoming more and more pressing that I keep my app up to date :)

So is there a workaround for workers, or is there a release where this prob is fixed ?

Thanks

All 45 comments

Starting off I'd like to point something out - that behavior IS expected.

The build-time exception is often a sign that something isn't quite right with the files - you have dupes, or you extend the same class, with the same name, meaning if the build was uninterrupted - only one of the two extended classes will pass, which may result in unexpected behavior/bugs runtime.

I think we can make it so the sbg will spit out a warning message when two of the same classes have the same content, and not throw, while still throwing an exception with that particular error message if contents of the two files differs.

@Pip3r4o i saw your branch about this issue, do you have a build i can test/use ?
i have a release pending on this :P
thanks in advance

@danielgek you can fetch the branch, and build the .jar locally, then simply replace it in build-tools/android-static-binding-generator. (navigate to android-static-binding-generator/project/staticbindinggenerator && ./gradlew build - the output jar will be in build/libs)

However these changes will not fix or work around the problem that @sis0k0 has described with webpacked workers. While the two files whose contents are being compared may look the same, there is a very important bit (JavaScriptImplementation annotation) that will fail the check.

@Pip3r4o i was already doing that xD, but yeah i got the same error, thanks for the help !!

I'm hitting this same issue when developing plugins that have @Interfaces() decorated classes. So I make changes to the local TS files for the plugin. Then go to the demo project and do a tns run android and very often I get this error because the static binding generated decides to create a new file. I guess happens because it detects that the plugin files have changed (i.e. installed a new version of the plugin) and decides to generate again the Java classes but does not clear the old ones. Also tns run android --clean does not seem to fix the problem and you have to do tns platform clean android to fix the problem.

In case of plugins the CLI/Runtime should detect that a new version of the plugin is used and should automatically remove the old java classes that have been generated by this plugin so the new ones can be generated successfully. Or it should just override the existing classes with the new ones.

@PeterStaev I recently pushed a change to the sbg logic to make it less aggressive during a build. That is achieved by always invalidating the list of to-be-generated classes.

Thanks @Pip3r4o ! I've just tried the @next of the android runtime and it is working really nice ๐Ÿ‘ Will speed up plugin dev tremendously! ๐Ÿ˜„

I was having an issue with a duplicated DropDownAdapter class from nativescript-drop-down and @next solved it.

I am still experiencing this issue when running npm run start-android-bundle with NS 3.1.1:

$ tns info
All NativeScript components versions information
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Component        โ”‚ Current version โ”‚ Latest version โ”‚ Information โ”‚
โ”‚ nativescript     โ”‚ 3.1.1           โ”‚ 3.1.1          โ”‚ Up to date  โ”‚
โ”‚ tns-core-modules โ”‚ 3.1.0           โ”‚ 3.1.0          โ”‚ Up to date  โ”‚
โ”‚ tns-android      โ”‚ 3.1.1           โ”‚ 3.1.1          โ”‚ Up to date  โ”‚
โ”‚ tns-ios          โ”‚ 3.1.0           โ”‚ 3.1.0          โ”‚ Up to date  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Did I understand wrong that this was supposed to be fixed in this version?

Having duplicate classes with the same name will fail your build, and that is expectrd, yes. Read the error carefully and see where the conflicts are.

@Pip3r4o thanks for the reply. As far as I understand, I encounter this issue due to a web worker that I am using in my plugin as is explained in this issue. Shouldn't web workers be supported out of the box?

I see that @sis0k0 explains that "you don't really need to import globals in your worker" so I tried not to include the globals in the worker, but even then I am getting the same error:

Running full build
finished with reading lines with js files
Warning: there already is an extend called com.tns.FragmentClass.
Warning: The static binding generator will generate extend from:vendor.js implementation
:asbg:generateBindings
Exception in thread "main" java.io.IOException: File already exists. This may lead to undesired behavior.
Please change the name of one of the extended classes.
File:/Users/joni/projects/nativescript-webpack-error-app/platforms/android/src/main/java/com/tns/FragmentClass.java Class: com.tns.FragmentClass
        at org.nativescript.staticbindinggenerator.Generator.writeBindings(Generator.java:68)
        at org.nativescript.staticbindinggenerator.Main.main(Main.java:15)
:asbg:generateBindings FAILED

Hi,

I believe I am having the same problem as @sis0k0 , trying to webpack an {N} app with a web worker:

Exception in thread "main" java.io.IOException: File already exists. This may lead to undesired behavior.
Please change the name of one of the extended classes.
File:/home/quentin/Projects/hubup-nativescript-clover/platforms/android/src/main/java/com/tns/FragmentClass.java Class: com.tns.FragmentClass
        at org.nativescript.staticbindinggenerator.Generator.writeBindings(Generator.java:68)
        at org.nativescript.staticbindinggenerator.Main.main(Main.java:15)
:asbg:generateBindings FAILED

I waited a few weeks before migrating to {N} V3, but now, it is becoming more and more pressing that I keep my app up to date :)

So is there a workaround for workers, or is there a release where this prob is fixed ?

Thanks

@quentinor we have not put any specific effort towards resolving this particular issue. We are still deliberating how to best handle the case with webpack, since it essentially pours the scripts you already use in the main bundle into the worker script bundle. The static binding generator does not, at this point, differentiate between a regular script, and one that will be used on a worker thread.

For the time being, avoid importing the tns-core-modules into a worker script, when webpacking. That should be sufficient to let the android build complete.

Hello Pip3r4o. You said:" For the time being, avoid importing the tns-core-modules into a worker script, when webpacking. That should be sufficient to let the android build complete."

How do you do that?

@leocrawf don't import 'globals' as it is used by console

In which Version is it fixed?

The fix is immediately available when using tns platform add android@next or the release candidate by using tns platform add android@rc.

The fix will also become available in the upcoming stable 3.2 release of the android runtime, coming next week.

In order for it to work, you need to use it in conjunction with the webpack worker-loader plugin as explained here -> https://github.com/NativeScript/worker-loader#install

I updated my project to 3.2.0. I'm still experiencing issues as described above when using var http = require("http"); in my worker.

@bfv for me it is working fine. Make sure you are using nativescript-dev-webpack v0.8.0.

I am using nativescript-dev-webpack v0.8.0. Strange.

@bfv yeah I had some issues as well, until I just created a new project and then copied my app dir and relevant files to the new project and did the installation of nativescript-dev-webpack from scratch. There are so many dev deps there that I wasn't sure everything was updated to the latest. Anyway eventually it started working.

@bfv @abhayastudios
You need these loader and plugin - https://github.com/nativescript/worker-loader.

Is this the official and permanent solution for worker threads and web-pack - or is this a temporary fix.

btw I commented out my global imports and I was still getting the FragmentClass refined error.

@erjdriver https://github.com/nativescript/worker-loader is the official way of getting workers working with AOT/Webpack

same problem, how to solve it ?

@Nurgunkalol please have a look at the README at https://github.com/nativescript/worker-loader explaining how to configure the worker loader for webpack.

I followed the instructions - developing in typescript.

I installed nativescript-worker-loader.

The webpack.config.js file was auto generated

Then I tried to include the worker file as:

import * as WorkerScript from "nativescript-worker-loader!./worker-script";

with worker-script replaced with my filename.

I'm getting a module not found error in vscode.

but if I ignore it and build/run it, I get the same redefinition FragmentClass error.

This is in android with 3.2

@erjdriver I'm pretty sure you need to feed a .js file as the worker. I use something like this:

if (global.TNS_WEBPACK) {
  let Worker = require("nativescript-worker-loader!./assets/js/phonebook-worker.js");
  worker = new Worker;
} else {
  worker = new Worker('./assets/js/phonebook-worker.js');
}

@abhayastudios the README page says support for typescript files.

@erjdriver TypeScript files are transpiled down to JavaScript, so you should have javascripts at runtime.

I'm using the syntax in the README file for ts worker files

import * as WorkerScript from "nativescript-worker-loader!./worker-script";

I understand TS compiles down to JS.

But in the app folder there's no .js files - even for my other files. I assume that's something webpack is doing - storing it somewhere else.

Issue #1 is that above import statement results in module-not-found error. Even though I've got the correct ts file in the app folder.

Issue #2 is at startup time, I get the FragmentClass redefinition error that the OP wrote about.

Here's the error that gets displayed when i try to webpack on the android

Note the error "not cacheable"

`[224] ../node_modules/nativescript-worker-loader!./worker-check-version.ts 119 bytes {0} [not cacheable] [built]
+ 320 hidden modules

ERROR in [at-loader] ./app/utils.ts:19:37
TS2307: Cannot find module 'nativescript-worker-loader!./worker-check-version'.`

When I try to include the file from /app/utils.ts file -I get the cannot-find-module error

@erjdriver as far as I know you need to explicitly reference the .js file (not the .ts file) since the .js file is what needs to be executed runtime. I don't know whether webpack transpiles typescript sources for workers as I wrote my worker in plain ES6. However, as a debug step, I would try to manually transpile your .ts file to .js and reference that one just to check whether it works.

Regarding the FragmentClass exception: are you sure you are using nativescript-dev-webpack ^0.8.0 in your package.json? If so, then try removing the android platform and adding it back again.

@abhayastudios thank you for your suggestions.

  1. dumb question - how do i force a transcompile. In my apps folder there are NO js files - even for the other files. I assume that's because webpack is doing something.

  2. i'm pretty sure - but i'll do your suggestion once I get past #1.

You can run tsc worker-name.ts which should produce the JavaScript version. You can also copy/paste the TypeScript code into their playground for an online transpilation.

@erjdriver since your worker is done in TypeScript, did you follow the README steps strictly? Did you make a typings file? -> https://github.com/nativescript/worker-loader#integrating-with-typescript

ok making some progress.

I added the typings folder in the root and am not getting the module-not-found error any more.

The build succeeds.

But when the app tries to run in the emulator.

I removed and added the android platform before the build.

`finished with reading lines with js files
Warning: there already is an extend called com.tns.FragmentClass.
Warning: The static binding generator will generate extend from:vendor.js implementation
Exception in thread "main" java.io.IOException: File already exists. This may lead to undesired behavior.
Please change the name of one of the extended classes.
File:C:\Users\MyPC\nativescript\test4b\platforms\android\src\main\java\com\tns\FragmentClass.java Class: com.tns.FragmentClass
at org.nativescript.staticbindinggenerator.Generator.writeBindings(Generator.java:68)
at org.nativescript.staticbindinggenerator.Main.main(Main.java:15)

FAILURE: Build failed with an exception.`

This issue is still not fixed - not sure why it was closed.

Still getting the duplicate FragmentClass error.

I've commented out all the Worker references and still getting the error.

@erjdriver please provide a minimalistic sample repository to help us debug the problem, as I am unable to reproduce it on my end.

@erjdriver did you try tns platform clean android, as @PeterStaev say? This command fixed FragmentClass error in my project.

Seems there is some problem with @next (3.3) version. I just got a PR for the Drop Down plugin to support Angular 4.4 (https://github.com/PeterStaev/NativeScript-Drop-Down/pull/138) and this failed the the CI build with the Fragment class error: https://travis-ci.org/PeterStaev/NativeScript-Drop-Down/jobs/286806719#L3749 .

@PeterStaev I am not sure it is a problem with the @next of the android runtime. I tested master branch of your plugin's demo (tns build android) with the latest versions of angular and the nativescript-angular plugin. The project built fine without exceptions.

@Pip3r4o seems you are correct. It could have been something specific to the specific version that was used in the PR. With the resubmited PR with @next there is no error ๐Ÿ‘

@erjdriver I created a minimalistic sample of loading a worker in typescript, following the nativescript-worker-loader README. You can find the sample in the pete/workertsbranch of the demo-worker repo -> https://github.com/NativeScript/demo-workers/tree/pete/workerts

@Pip3r4o - thanks for getting back to me and taking the time to create the test project.

I solved the problem.

The problem was that in my bundle-config.ts file, I had

global.registerModule("nativescript-telerik-ui/sidedrawer", function () { return require("nativescript-telerik-ui/sidedrawer"); });

global.registerModule("nativescript-telerik-ui/sidedrawer/drawerpage", () => require("nativescript-telerik-ui/sidedrawer/drawerpage"));

I was getting the multiple definition of FragmentClass.

If I comment out the second line - then it works.

Weird because I got those two lines from some sample projects on github.

Hopefully this will help someone else...

Was this page helpful?
0 / 5 - 0 ratings