Use Custom Toolchain (ARMCC instead of GCC)

I want to use an ARMCC compiler instead of default GCC one. Is there any possible way to do this? I haven’t found any specific option for .ini file. I have only found (here) a short .py script that should (presumably) replace compiler with default option. However, I think this only works with different GCC versions and not completely different compiler.

Here’s the the build log, which is executed after GCC build + link (which wasn’t really expected, as I though the GCC toolchain path used to build project would be replaced alltogether with ARMCC path):

The default flags and builder scripts are all hardcoded for GCC.

So you’ll also have to repoint the “$CC”, “$AS”, “$LD” etc variables. And replace any flags that won’t work on ARMCC with equivalent ones or remove them.

This is perfectly possible, but not officially supported. See e.g. GitHub - maxgerhardt/platformio-with-clang: Highly experimental PlatformIO + Clang (instead of GCC) test project for inspiration.


My attempts to use different compilers:

1 Like

After replacing flags (and removing the ones from original project for clang) and paths, I only manage to get this error. Please note that I don’t have this .json file in my workspace, however should there be one? If yes, what should it have inside of it? Anything related to the compiler I’m trying to set up?

P.S.: I didn’t go into detail about flags and just copied the basic ones from Keil uVision, which was my previous IDE for this project. I hope they are OK (for now). Although this isn’t the source of currently unresolvable error.

If you want to use PlatformIO package management, you’ll have to create a proper package.json for toolchain-armcc. That’s just a metadata file, giving it the right name (and optionally version) is already enough. Also, the package.json should be placed in the root of the toolchain, not in bin/.

You could e.g. try something like

  "name": "toolchain-armcc",
  "version": "1.50000.200702",
  "description": "ARMCC compiler",
  "keywords": [
    "build tools",
  "homepage": "",
  "license": "GPL-2.0-or-later",
  "system": [
  "repository": {
    "type": "git",
    "url": ""

it’s also to be recommended to use symlink:// instead of file:// in the platform_packages to avoid doing a copy to internal PlatformIO folders.

Further reading on PlatformIO internals is e.g. here: Arduino Due (or other SAM3/4) upload fails with ATMEL ICE JTAG on VSCode with PlatformIO - #7 by clemens

1 Like

And before you run into more problems with it: I see you using e.Prepend(CCFLAGS..) etc. That will add these flags to the front of the command, but the old GCC commands will still be in there. You might want to consider env.Replace() (but this might cause problems in very specific scenarios where scripts also add onto the build flags, so you might better of just carefully filtering out / converting compiler flags).

I used the symlink:// as you recommended. And put package.json into bin folder - right now, I cannot put it into the root C: because I don’t have administrator rights (however if this is the core of error described, I will try to put it there somehow). I get the following errors.

Why am I getting error about flags that should be filtered out? Even if I uncomment the other filters for other flags, the error log stays the same. And no effect on the error log have any of newly added ARMCC flags.

I think I have tried removing options “-all” and “-cpu cortex-m3” from all possible flags. But it seems those flags persists somewhere in some sections I haven’t addressed yet. And judging from how many times same error appears, I would say there are multiple of such occurences.

No no, the root of the toolchain is not C:\. It’s C:\Keil_v5\ARM\ARMCC. Just the folder without the bin.

The "-cpu cortex-m3" filter as one string look weird. Usually arguments are split over the spaces, so it would appear as separate "-cpu" and "cortex-m3" arguments.

Try to use the PlatformIO core CLI and the

pio run -v -j1

to do a verbose, single-threaded compilation. You will see all the flags that are in the command and can work on getting them filtered or replaced correctly.

Yes, that’s what I have initially tried but didn’t work. The packet.json was found only when it was put into bin folder.

I’m not sure. ARM documentation for compiler options provides these two ways of using the cpu option: --cpu=name and --cpu=list. Therefore I would say option and its argument come as one thingy. But I’m not knowledgable in this field to say for sure.
Anyway, I tried your way, providing filtered flags as ["-cpu", "cortex-m3"] but the error log remains the same. Note that CubeMX Makefile (which I’m using as a reference) provides its cpu option as -mcpu=cortex-m3.

This is what I get. Should this be built with GCC? Because here I think it uses ARMCC. Since its giving error for -all, maybe I should filter out -Wall? Not exactly the same but similar.

Yes, filtering -Wall seems to remove at least -all option that is being referred to by an error.

If you can compile the STM32 Cube project in Keil 5 using the ARMCC compiler, just do a verbose compilation in there so you know the exact compiler invocation that that IDE does, then massage the PlatformIO script to match it.

Also -mthumb and -mcpu=cortex-m3 might be GCC specific, filter them and prepend the needed flag for ARMCC (should be discoverable per above).

Yes, you’re right. I inspected ARMCC compiler inline options some more and dealt with these flags. I’m still dealing with these flags.

There is still one thing I question right now and can’t identify its source. Some options, which I provided under all available flags, still come up in the build log in the form:
Warning: C3910W: Old syntax, please use '-M'.
Warning: C3910W: Old syntax, please use '--thumb'.
Warning: C3910W: Old syntax, please use '-S'.

And these are repeating multiple times througout the build log. Why is that? I provided these flags already. Why, with some specific .o file I get these errors? Where more could/should I provide these missing flags?

P.S.: If you check my current flag options setup, you will see these flags inserted where they belong.

Additional question. Why are new compiler’s (in my case ARMCC) flag options first prepended to array of existing (by default GCC) options and then the non-valid GCC options are filtered out before each flag variable is replaced with its old value? If I understand correctly, given the e object’s methods, couldn’t one just replace old flags with new flags and avoid two step process like it’s implemented in this script?

I’m asking because I added my specific ARMCC asm, linker and compiler options, but now I there is no end to existing GCC flags I need to filter out. :joy:

Sure, you could also compute the new values directly instead of prepend + filter as a 2 step process – the clang thing was just something I cobbled together as a proof of concept, don’t take it as authoritative on best PIO scripting practices.

The GCC invocation still as -mthumb in it, no, maybe that’s where the errors are coming from?

Also, don’t you have to use a different startup file for the ARMCC toolchain, or rather, its assembler, then?

Same for the linker script.

Maybe if I completely override existing flag options completely even -mthumb might get completely removed. But it seems this isn’t the issue. Unless there are some other flag variables which I (your clang project actually) didn’t consider.

I had issues with startup file actually. I originally had a startup file from CubeMX, but saw later that I get errors for each line of it - that’s because CubeMX generates startup file for mainstream arm-none-eabi toolchain and I’m using MDK-ARM toolchain. Therefore, I took a startup file from Keil uVision 4 and, as expected, works without startup file errors. I only have a bunch of GCC flags to filter out (or overwrite).

I left linker file as the one I got from CubeMX. So far its specific contents seem trivial. And linker file is probably not compiler-specific (but rather target-specific), maybe? Why are you interested in which startup (aka assembler) and linker file I use? Would they have to do something with compiler/linker/assembler options flags?

Btw, did you intentionally not override flags and re-used some of basic ones from GCC that are the same for Clang and might come handy for Clang as well? If so, maybe I should leave project as is and remove flag options until there is nothing left (from error log) to remove! :smile:

example using pre-installed Microchip X32 - is GCC but other compiler

Doesn’t CubeMX have multiple target IDEs in the project generation tab? One of them is Keil maybe?

I don’t think so. The .ld file is specific to the gnu linker (ld) that’s internally invoked when gcc/g++ does the link.

Look at

you’ll see 3 folders, one for each toolchain. The ARM one has a .sct file as the linker script, GNU GCC has the .ld, IAR has a .icf.

CubeMX has multiple IDE options for project files generation. However only for “Makefile” option it generates linker, startup and makefile files. In other cases it probably assumes that specific IDE takes care of these things. At least for Keil, it does exactly that. It has linker and startup files for each target stored somewhere in its root folder.

@maxgerhardt I would kindly ask for additional help if I may. As you see, I slightly modified the original (clang) project for using custom compiler instead of built-in GCC one. As the thing concerns flags, first I overwrite all existing flags using empty array (no strings) and then I write those flags which seem relevant/required. The issue that remains is that the GCC options, which I think are library-relevant, keep popping up because they aren’t removed. It seems there might be more flag variables hardcoded into PlatformIO’s makefile? Can you quickly check and see whether there are any flag variables I haven’t considered yet and might have access to options from error log?