This is the best method I have come up with recently:
In the build options, select create batch file.
When you initiate a build from the IDE, a batch file along with several text files are created based on the options set in the IDE. You need to track these IDE generated files in source control:
- *.bat
- *.ini
- *.__i
- *._ia
- *.lnp
- *.sct
Then foo.bat can be launched from a build script.
While this does create extra files that need to tracked in source control if you want to build reliably from the generated batch file, it does remove the need to rely on the Keil project file (foo.uvproj) and the IDE. I find it easier to compare differences, and thus track changes, to the generated text files (*.__i) that contain compiler flags than to the .uvproj file. Additionally, the batch file calls the various tools, armasm, armcc, armlink, directly. This gives you the direct output of each of those steps as well as a seemingly better potential for migrating a project to a different tool chain in the future if necessary.
I realize this answer sounds a lot like my original question, but I genuinely don't know of a better way of running a scripted build with Keil's tools. I asked to see what might come up from others. I don't completely disagree with the answer from @digikata, but I prefer to have compiler flags and the memory map in an easier format for tracking and to use more unix-style tools for compilation rather than launching an all-in-one compilation with the IDE. I think the all-in-one compilation from the IDE works well at my workstation, but not for the build server.
EDIT: The build server runs on Windows Server 2003. I must confess that I have relented to using the IDE command line interface rather than a batch file. This just became too difficult to manage.
To compile from C code to an executable image that you can run on an ARM microcontroller, there are two steps: compile, and link.
During compile, the C code will ultimately be turned into object code, which is not usable on its own. In the middle of the process, you may be able to generate intermediate assembly listings for the C files.
During link, the object files will all be combined together and the function calls and global variables will be linked. It also decides where in the target's memory space everything will end up (code, read only data, read/write data).
This is how you can call something like printf
without having the code in your file. When you compile, it doesn't know anything about printf
except for the prototype in stdio.h
. When linked, the linker will fill in the details about where printf
actually lives in memory.
The linker uses a scatter file to know where in the target certain memory areas exist. Typically a microcontroller will map internal Flash to address 0x00000000
. Internal SRAM will exist at some other address, say 0x20000000
, and external DRAM or Flash could exist at another address, 0x80000000
. The scatter file will inform the linker of these areas and direct it to place certain sections of the output in certain areas. Typically, your executable code lives in the text
section, which would typically be stored in your internal flash. Your read only data would live in the section called rodata
and also be stored in flash. Read/write data will typically be in bss
(initialized to zero) or data
(initialized with value), and this would be placed in RAM, with initialized values being stored in another section in flash.
These section names are somewhat toolchain specific, though they are usually similar across tools.
The linker map file is an output file that tells you after linking where all your symbols have ended up. For example it may tell you that main()
has been located at 0x00000278
in Flash, and that the global variable int x
has been located at 0x20001012
in RAM. It will also tell you where linker-generated sections have been placed. These include your C stack, potentially an interrupt stack, and also possibly a heap. The scatter file usually allows you to specify the size of these sections as well.
Best Answer
Soo that The definition and implementation can be overridden with a non weak type, aka user defined one.
Check the manual to bee suit..