How to deal without DFX?

I am very much pissed off calling DFX that compiles not only what I need to compiler but also I don’t need to compile and when I don’t want to compile anything (e.g. I can’t turn off recompiling everything when I run dfx generate), inflating compilation times to giant amounts.

We should use normal Makefile workflow, DFX needs to be excluded.

I want to write a Makefile. But how to do dfx generate without DFX? Does DFX call something to do dfx generate or is it a built-in DFX functionality?

Hi @qwertytrewq

didc is the tool that generates the bindings, and it is available standalone from the candid repository here (latest release). You can generate bindings by calling didc bind.


Having said that, did you present your feedback of what exactly should be improved about dfx somewhere? I’m sure the SDK team would be interested.

While dfx is definitely not perfect, it can be improved. IMHO the better solution is to improve dfx such that you don’t have to use didc directly. There is also a feedback board, which would probably be the best place to request improvements.

dfx recompiles everything when I request it to do something, e.g. to generate. This takes a lot of time.

Also, if I want to recompile a canister, I need to recompile all canisters, because otherwise canisters other than our dependencies become missing in .env (at least so it was with a previous version of DFX).

Compiling a big project is almost a nightmare due to these “features” causing outspoken slowness of compiling.

Should I copy this to the feedback board?

Should I copy this to the feedback board?

Thanks @qwertytrewq. Yes, I think that’s the right way to go.

@Severin: do you agree?

Thanks for voicing your frustrations, we don’t get enough feedback about such usability issues with dfx. And please add it to the feedback board!

dfx builds Motoko canisters (but not others) as part of dfx generate because it is the only canister type that doesn’t force the user to specify a .did file. As a result if dfx generate wouldn’t build the canister (read: the did file) automatically, then

  • dfx generate would fail if the user did not dfx build some time before because the did file is missing
  • IMO more importantly: dfx generate would produce the wrong bindings if method signatures changed between the last dfx build and running dfx generate, resulting in errors that would be pretty hard to debug for non-experts

I’m personally not a fan of not automatically building because of the second point, but if you disagree please explain. It would be possible to add a flag like --no-build to dfx generate, but honest question: would you have found out about that flag? E.g. with dfx generate --help? I never know how reliably people check documentation

That shouldn’t be necessary if you declare the canisters.<canister>.dependencies key in dfx.json properly

We use didc directly for the nns-dapp and sns-aggregator. It works well in standalone mode, for our purposes anyway. It has needed some tweaks but the team that maintains it has been very helpful at getting things fixed.

dfx generate produced several files per actor, as:

example_backend.did.d.ts  example_backend.did.js  index.d.ts  index.js

But

didc bind -t ts XXX.did

outputs only one file (to stdout).

How to create the same several files as dfx generate produced?

I am afraid I have no idea. Does it help to run -t ts and -t js? I’m not a front end guy so I typically have to stare at those files to figure out what on an earth they contain. :sweat_smile: Maybe @lmuntaner can help?

If you take a look at https://internetcomputer.org/docs/current/developer-docs/developer-tools/cli-tools/dfx-json-reference, and specifically CanisterDeclarationsConfig, it states

A list of languages to generate type declarations. Supported options are 'js', 'ts', 'did', 'mo'. Default is ['js', 'ts', 'did']

dfx reads the canisters in your project from dfx.json and then, by default, uses didc to generate bindings for JavaScript, TypeScript, and Candid

All you strictly need is the idlFactory. dfx creates the index as a helper, but you’ve clearly progressed beyond the beginner use case it’s suited for. Just write your own createActor method and import the idlFactory from

didc bind -t js XXX.did

I do like:

import { idlFactory as mainIdlFactory, ZonBackend } from "../../out/src/backend/main";
...
const MainCanister: ZonBackend = Actor.createActor(mainIdlFactory, {canisterId: process.env.CANISTER_ID_MAIN!})

and get:

Uncaught (in promise) TypeError: interfaceFactory is not a function
    at Actor.createActorClass (actor.js:111:1)
    at Actor.createActor (actor.js:137:1)
    at App.tsx:58:39

What do I do wrong?

Solved by generating both .js and .d.ts files.

Makefiles can give you more control. To skip DFX for generating, check its documentation or source code to find the command it uses internally. Then, call that command directly in your Makefile.