Building Ada enabled mingw64 compilers

mikepm
2012-10-17
2013-07-31
  • mikepm
    mikepm
    2012-10-17

    I'm currently looking at using Mingw64 with msys on Windows. Although there are many mingw64 32/64 bit packages available for running on Windows with msys, I've not come across any including ada. As a result I've been exploring what is involved in creating these via a 32 bit Ubuntu 12.04 installation.

    I've now been able to build ada enabled versions of the 32 bit Windows native compiler and the 32 bit Windows native cross compiler for Windows 64 - and they seem to work. I think the approach could also be used to build toolsets targeting other host/target combinations. I am including a full description here in the event that it will be of interest to others.

    Given that I have made some source code changes for this to work, the built systems would need full testing, especially in regard to the ada tools, to make sure they function fully as they should. I have only done an exceedingly simple test on the latter - building "helloworld.adb". At this point I don't know how to do a full gcc test.

    All this work is based on the use of Rubenvb's marvellous build scripts.

       https://github.com/rubenvb/MinGW-w64-build-scripts

    These gave me a well written, flexible, reliable, easy to use build system that enabled running of the entire complex build process for multiple targets using the buildall.sh script. They also gave me total control over the various build options via the other scripts. I would like to express my deep appreciation to him for making the scripts freely available on the internet.

    The official build of Ubuntu 12.04 comes with GCC 4.6.3. Eventually I found that I needed a gcc 4.7.2 native Linux version as all the work I did with Ruben's scripts was based on the GCC 4.7.2 release. I built this from the 4.7.2 release source using the guide provided by Solarian Programmer, and again I am very grateful for the guidance this page gave me.

       http://solarianprogrammer.com/2012/04/13/building-gcc-4-7-on-ubuntu-12-04/

    In using Rubenvb's scripts I made the following changes

    a) in settings.sh : modified the -enabled-languages setting to include Ada

    b) in settings.sh : eventually modified MAKE_OPTS to use "-j1" instead of "-j4" (see below for reasons behind doing this)

    c) in settings.sh : changed <export BUILD="x86_64-linux-gnu"> to <export BUILD="i686-linux-gnu"> as I'm running a 32 bit system

    d) in directories.sh : made changes to reflect the top level directories I used on my system

    e) In the buildall.sh script I only wished to run following of his build scripts, as I can only run 32 bit systems on Ubuntu and Windows

       buildlinux32mingw32.sh
       buildlinux32mingw32.sh
       buildmingw32mingw32.sh
       buildmingw32mingw64.sh

       I commented the others out.

    f) I also modified the scripts so they pointed at a gcc source tree named gcc-mod rather than gcc. I did this because I found it necessary to make some changes to the source code to resolve problems that I encountered in building the 32 bit Windows toolchains. I used the original unamended gcc source tree for building the 32 bit Linux native version of the compiler.

    g) Eventually I also modified the buildall.sh script to point to the Linux native 32 bit GCC 4.7.2 release (See below for reasons)

    Building the 32 bit Linux to 32 bit mingw64 ada enabled cross compiler

    To build this successfully I had to do two things

    1) use a GCC native version with exactly the same version number as the version of GCC I used to build the cross compiler

    2) use -j1 for the MAKE_OPTS rather than -j4

    Initially I tried to build the GCC 4.7.2 based mingw64 cross compiler from my installed GCC 4.6.3 compiler. This resulted in compile errors in the ada build phase. The error I got was

    scng.adb:1799:47: "SPARK" is undefined
    

    followed further down by

    gnatmake: "/home/mike/mingw64-sources/gcc/gcc/ada/ali-util.adb" compilation error
    make[3]: *** [gnatmake-re] Error 4
    make[3]: Leaving directory `/home/mike/mingw64/linux32mingw32/gcc/gcc/ada/tools'
    make[2]: *** [gnattools-cross] Error 2
    make[2]: Leaving directory `/home/mike/mingw64/linux32mingw32/gcc/gnattools'
    make[1]: *** [all-gnattools] Error 2
    make[1]: Leaving directory `/home/mike/mingw64/linux32mingw32/gcc'
    make: *** [all] Error 2
    

    Googling the issue I found that to build an ada cross compiler one must use the same GCC version for the native and cross compilers. Once I switched the buildall.sh script to point at the GCC 4.7.2 native version I had installed, instead of the Ubuntu default 4.6.3 version, the ada compile steps worked and the build/install process completed. However there was still a hidden problem as discussed next.

    At first I was running the build and install process using the -j4 MAKE_OPTS setting in the scripts. With this setting I found that the build and install processes always completed. However most such runs produced an ADA compiler that didn't work, while a few runs produced a compiler that did work (I could find no pattern in this). For the occasions when the ada compiler wasn't working, the results I got were

    linux32mingw32/mingw32/bin/i686-w64-mingw32-gnatmake helloworld.adb
    i686-w64-mingw32-gcc -c helloworld.adb
    i686-w64-mingw32-gcc -c -I./ -I- /home/mike/mingw64/linux32mingw32/mingw32/lib/gcc/i686-w64-mingw32/4.7.2/adainclude/text_io.ads
    i686-w64-mingw32-gnatbind -x helloworld.ali
    i686-w64-mingw32-gnatbind: Cannot find: s-stalib.ali
    i686-w64-mingw32-gnatmake: *** bind failed.
    

    The cause of this turned out to be an incomplete install of the mingw32/lib/gcc/i686-w64-mingw32/4.7.2/adainclude folder and a completely missing mingw32/lib/gcc/i686-w64-mingw32/4.7.2/adalib. Eventually I tracked this down as being due to using -j4 for the MAKE_OPTS. There appear to be timing or dependency issues involved when using this setting - resulting in the build/install problem. Switching to using-j1 for MAKE_OPTS eliminated this problem and with this setting runs of build/install consistently installed these folders in full and a test of the ada compiler using gnatmake generated the "helloworld" executable - which worked as expected. I later came across a web reference suggesting that cross compiler builds including ada MUST use a make option of -j1 rather than other values.

    Building the 32 bit Linux to windows 64 bit mingw64 ada enabled cross compiler

    Using the same changes from the previous section, the Linux 32 bit cross compiler for Windows 64 bit also built without any apparent problems. I could successfully create the helloworld.exe via gnatmake, but cannot test it actually runs as I don't have a 64 bit Windows system.

    Initial unsuccessful attempt to building a 32 bit mingw64 Windows native ada enabled compiler

    I encountered two problems

    a) The build process failed due to not being able to find xgntugn. This file is built in the ada/doctools folder and used to create modified gnat_ugn.texi and projects.texi files, the processes being defined in "Make-lang.in" from the ada/gcc-interface folder in the gcc source tree.

       doc/gnat_ugn.texi: $(srcdir)/ada/gnat_ugn.texi $(srcdir)/ada/ug_words \
       doc/projects.texi $(gcc_docdir)/include/gcc-common.texi gcc-vers.texi
        ada/doctools/xgnatugn unw $(srcdir)/ada/gnat_ugn.texi \
           $(srcdir)/ada/ug_words doc/gnat_ugn.texi
       doc/projects.texi: $(srcdir)/ada/projects.texi
        $(MAKE) ada/doctools/xgnatugn$(build_exeext)
        ada/doctools/xgnatugn unw $(srcdir)/ada/projects.texi \
           $(srcdir)/ada/ug_words doc/projects.texi
    

    As the build process is for a Windows native build it actually creates xgnatugn.exe, which is what would be expected. On a Windows machine the code would find this and hence work. However on a Linux machine it expects simply xgnatugn and the build stops when it cannot find it.

    WORK ROUND: Copy xgnatugn from the ada/doctools folder in the Linux mingw64 cross compiler build folder into the this folder in the Windows build folder. Then run the buildall.sh script which will pick up processing from this point and now produce the final gnat_ugn.texi and projects.texi files.

    For running the build repeatedly this could quickly get tedious, so I copied the xgnatugn executable from ada/doctools in the gcc 4.7.2 native Linux build folder to the gcc-mod/gcc/ada source folder.

    I also edited "Make-lang.in" in the gcc-mod/ada/gcc-interface folder, changing

      ada/doctools/xgnatugn$(build_exeext): ada/xgnatugn.adb
        -$(MKDIR) ada/doctools
        $(CP) $^ ada/doctools
        cd ada/doctools && $(GNATMAKE) -q xgnatugn
    

      to

      ada/doctools/xgnatugn$(build_exeext): ada/xgnatugn.adb
        -$(MKDIR) ada/doctools
        $(CP) $^ ada/doctools
        cd ada/doctools && $(GNATMAKE) -q xgnatugn
        if [ ! -e xgnatugn ]; then \
            cp $(srcdir)/ada/xgnatugn ada/doctools/xgnatugn; \
        fi;
    

    so that xgnatugn would be copied in if not built, and hence prevent the build falling over.

    b) The build process next failed during the build of the gnat tools

    This was the more serious issue.

    This failure is due to the way the building of the gnat tools is handled by the "configure" and "make.in" files in the gnattools folder within the gcc source tree. This code does not appear to be suited to the building of non-Linux gcc toolchains on a Linux platform, though it does work when building Linux native compilers and Linux based cross compilers. A look at the code from "configure" will help explain this

    # Determine what to build for 'gnattools'
    if test $build = $target ; then
      # Note that build=target is almost certainly the wrong test; FIXME
      default_gnattools_target="gnattools-native"
    else
      default_gnattools_target="gnattools-cross"
    fi
    

    This code, and the code in "make.in", seems to implicitly assume that one is building gcc for the case where $host = $build. Thus it works when building of native compilers and native cross compilers targeting other systems. Both the "gnattools-native" and "gnattools-cross" options ensure that when the gnat tools are built they always use the ada run time for $build i.e. from the existing native compiler installation - the value of $host has no influence on this. The "gnattools-cross" make option above additionally ensures that the gnat tools get installed with a cross compiler prefix.

    The crux of the problem is that in any gcc build, the ada runtime built is based on the $target settings, so that in the new gcc installation the ada compiler will generate code appropriate to that target. However when building the gnat tools one needs access to an ada runtime specific to the host they will run on i.e. an ada run time for which $target is the same as $host. For a native compiler they are the same but for cross compilers one will need to get the required ada run time from outside of the present build. In addition, $host may not necessarily be the same as $build. So the value of $host really does need to be taken into account when building the gnat tools.

    When building non-Linux systems on Linux the code fails because it forces all of them through the gnattools-cross make option ($target will always be different to $build). This make option always tries to use the native Linux ada runtime rather than a run time appropriate to the $host - hence the build fails. Even if it succeeded, it would result in all non-Linux builds ending up with cross-compiler prefix attached to the gnat tools. In the case of, for example, a Windows native compiler, this is clearly incorrect. The gnattools-native make option also points at a Linux ada run time so neither can it be used when building non-Linux systems.

    When building Windows 32 based toolchains one needs access to a Windows 32 ada runtime to build the gnat tools. This is true both when building a Windows 32 bit native compiler and also when building a Windows 32 bit cross compiler for creating Windows 64 bit applications.

    As a Windows 32 bit native compiler build will be targeting 32 bit Windows, it will be building a 32 bit ada run time and this can also be used for building the gnat tools. However when building the 32 bit Windows cross compiler for Windows 64, it will be generating an ada runtime for 64 bit Windows, which cannot be used to build the required 32 bit gnat tools. Instead one needs access to the ada runtime from the 32 bit Windows native compiler build in order to build these gnat tools. Hence the native compiler always has to be built first in order to provide this run time, and then one must somehow provide a link to it.

    Similar considerations would apply to building gcc for other non Linux host/target combinations.

    Final Successful build of a Windows 32 bit native ada enabled mingw64 compiler and cross compiler for Windows 64

    Having identified the issues as discussed in the previous section, I now looked at modifying the "configure" and "make.in" files in the source gcc-mod/gnattools folder to enable building of the Windows 32 bit native compiler and the Windows 32 bit cross compiler for Windows 64. I think the approach can be easily be extended to cover the same tools for a Windows 64 bit platform etc.

    After making the changes I describe below, I was able to successfully build both the Windows 32 bit native ada enabled compiler and the corresponding Windows 32 bit cross compiler for Windows 64. After transferring the mingw32 and mingw64 install folders to my Windows 7 32 bit machine I could successfully build the "helloworld.adb" application with the relevant gnatmake. The 32 bit version of hello.exe worked as expected, but as I am unable to run the 64 bit version I couldn't actually try it out.

    Changes to the gnattools "configure" and "make.in" files

    NOTE - the changes described below are based on the host and target names used in Ruben's scripts (BUT SEE WARNING NEXT) and won't work with other names, and are also intended to be used in the context of Ruben's scripts. If used in other ways then the changes may well need revision.

    WARNING - even though the Ruben script options for the 32 bit Linux cross compilers specify host=i686-linux-gnu, the gcc configure script turns this into i686-pc-linux-gnu. This happens because gcc configure script calls config.sub to validate the host, build and target values. That changes the Linux names. The i686-w64-mingw32 and x86_64-w64-ming32 values are left unchanged. Other values for host/build/target might also get changed so it is important to check for this before adding tests for other hosts into the code below. They can be checked simply by running config.sub e.g.

    gcc/config.sub "i686-linux-gnu"
    

    returns

    i686-pc-linux-gnu
    

    When running the builds it is also a good idea to check the configure logs to make sure they contain the values you expect.

    WARNING - if the revised configure script doesn't contain the right host values then you get unpredictable results due to default_gnattools_target not being set correctly.

    a) Changes to the "configure" file

    I changed the original code from

    # Determine what to build for 'gnattools'
    if test $build = $target ; then
      # Note that build=target is almost certainly the wrong test; FIXME
      default_gnattools_target="gnattools-native"
    else
      default_gnattools_target="gnattools-cross"
    fi
    

    to

    # Determine what to build for 'gnattools'
    if test $host = "i686-pc-linux-gnu" ; then
      if test $host = $target ; then
         default_gnattools_target="gnattools-native"
      else
         default_gnattools_target="gnattools-cross"
      fi
    fi
    if test $host = "i686-w64-mingw32" ; then
      if test $host = $target ; then
         default_gnattools_target="gnattools-new-native"
      else
         default_gnattools_target="gnattools-win32-cross-to-win64"
      fi
    fi
    

    Note that the new configure logic is based on running within a 32 bit Linux system. For a 64 bit Linux system the initial test would need to be changed to refer to x86_64-pc-linux-gnu instead of i686-pc-linux-gnu.

    The revised version uses the existing gnattools-native and gnattools-cross sections in "make.in" for building the Linux 32 native and Linux 32 to 32/64 bit mingw64 cross compilers. It also introduces two new make sections (added to "make.in" file) to handle the Windows 32 bit native compiler (gnattools-new-native) and the Windows 32 bit to Windows 64 bit cross compiler (gnattools-win32-cross-to-win64).

    Sections for other platforms could be added in a similar way.

    b) changes to "make.in" file

    First I  modified the makefile targets section, changing

    .PHONY: gnattools gnattools-native gnattools-cross regnattools
    

    to

    .PHONY: gnattools gnattools-native gnattools-cross gnattools-new-native gnattools-win32-cross-to-win64 regnattools
    

    Next I added the following two sections to define the build variables for the Windows 32 bit native compiler and the cross compiler for Windows 64. The NEW-NATIVE version simply points to its own ada run time in the ada/rts folder. So in principle this could also be used in the case of a Windows 64 bit native compiler. For the WIN32-CROSS-TO-WIN64 variables I have hard coded the location of the rts of my Windows 32 native compiler build as the cross compiler also needs to use this version. Since the cross compiler build relies on this existing it is therefore necessary to ALWAYS first build the Windows 32 bit native compiler in order to build the cross compiler. As that hardcoded reference is also specific to my system it would need to be modified accordingly if used elsewhere.  These sections are copies of the TOOLS_FLAGS_TO_PASS_CROSS section with just a modification to the ADA_INCLUDES option to point to the appropriate location for the ada runtime (first parameter in list).

    # Variables for gnattools, new native - gnat tools that will be used on a different system to the build system
    TOOLS_FLAGS_TO_PASS_NEW_NATIVE= \
        "CC=$(CC)" \
        "CFLAGS=$(CFLAGS) $(WARN_CFLAGS)" \
        "LDFLAGS=$(LDFLAGS)" \
        "ADAFLAGS=$(ADAFLAGS)"    \
        "ADA_CFLAGS=$(ADA_CFLAGS)" \
        "INCLUDES=$(INCLUDES_FOR_SUBDIR)" \
        "ADA_INCLUDES=-I../rts $(ADA_INCLUDES_FOR_SUBDIR)" \
        "exeext=$(exeext)" \
        "fsrcdir=$(fsrcdir)" \
        "srcdir=$(fsrcdir)" \
        "GNATMAKE=gnatmake" \
        "GNATLINK=gnatlink" \
        "GNATBIND=gnatbind" \
        "TOOLSCASE=cross" \
        "LIBGNAT="
    # Variables for gnattools, win 32 cross to win64  - gnat tools that will be used on Windows 32 with win 32 to win 64 cross compiler
    TOOLS_FLAGS_TO_PASS_WIN32_CROSS_TO_WIN64= \
        "CC=$(CC)" \
        "CFLAGS=$(CFLAGS) $(WARN_CFLAGS)" \
        "LDFLAGS=$(LDFLAGS)" \
        "ADAFLAGS=$(ADAFLAGS)"    \
        "ADA_CFLAGS=$(ADA_CFLAGS)" \
        "INCLUDES=$(INCLUDES_FOR_SUBDIR)" \
     "ADA_INCLUDES=-I/home/mike/mingw64/mingw32mingw32/gcc/gcc/ada/rts $(ADA_INCLUDES_FOR_SUBDIR)" \
        "exeext=$(exeext)" \
        "fsrcdir=$(fsrcdir)" \
        "srcdir=$(fsrcdir)" \
        "GNATMAKE=gnatmake" \
        "GNATLINK=gnatlink" \
        "GNATBIND=gnatbind" \
        "TOOLSCASE=cross" \
        "LIBGNAT="
    

    Finally, after the "gnattools-cross: $(GCC_DIR)/stamp-tools" make section I added the next two sections to handle the Windows 32 native and cross to Windows 64 gnat tool builds. These are basically copies of the gnattools-cross section. All I've done is changed the TOOLS_FLAGS references to point to my new versions above, and removed all the mv steps in the case of the gnattools-new-native section.

    # For building gnat tools to be used when with a native compiler that runs on a non linux system
    # it uses the ada runtime that is part of its own build for building the gnat tools
    gnattools-new-native: $(GCC_DIR)/stamp-tools
        # gnattools1-re
        $(MAKE) -C $(GCC_DIR)/ada/tools -f ../Makefile \
          $(TOOLS_FLAGS_TO_PASS_NEW_NATIVE) INCLUDES="" \
          gnatmake-re gnatlink-re
        # gnattools2
        $(MAKE) -C $(GCC_DIR)/ada/tools -f ../Makefile \
          $(TOOLS_FLAGS_TO_PASS_NEW_NATIVE) common-tools
    # For building gnat tools to be used by windows 32 to windows 64 cross compiler
    # It makes use of the ada runtime within the win32 native compiler build
    # This section won't work if the win 32 native compiler ada run time is unavailable
    gnattools-win32-cross-to-win64: $(GCC_DIR)/stamp-tools
        # gnattools1-re
        $(MAKE) -C $(GCC_DIR)/ada/tools -f ../Makefile \
          $(TOOLS_FLAGS_TO_PASS_WIN32_CROSS_TO_WIN64) INCLUDES="" \
          gnatmake-re gnatlink-re
        # gnattools2
        $(MAKE) -C $(GCC_DIR)/ada/tools -f ../Makefile \
          $(TOOLS_FLAGS_TO_PASS_WIN32_CROSS_TO_WIN64) common-tools
        # Rename cross tools to where the GCC makefile wants them when
        # installing.  FIXME: installation should be done elsewhere.
        if [ -f $(GCC_DIR)/gnatbind$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatbind$(exeext) $(GCC_DIR)/gnatbind-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatchop$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatchop$(exeext) $(GCC_DIR)/gnatchop-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnat$(exeext) ] ; then \
          mv $(GCC_DIR)/gnat$(exeext) $(GCC_DIR)/gnat-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatkr$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatkr$(exeext) $(GCC_DIR)/gnatkr-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatlink$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatlink$(exeext) $(GCC_DIR)/gnatlink-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatls$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatls$(exeext) $(GCC_DIR)/gnatls-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatmake$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatmake$(exeext) $(GCC_DIR)/gnatmake-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatmem$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatmem$(exeext) $(GCC_DIR)/gnatmem-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatname$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatname$(exeext) $(GCC_DIR)/gnatname-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatprep$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatprep$(exeext) $(GCC_DIR)/gnatprep-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatxref$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatxref$(exeext) $(GCC_DIR)/gnatxref-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatfind$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatfind$(exeext) $(GCC_DIR)/gnatfind-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatclean$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatclean$(exeext) $(GCC_DIR)/gnatclean-cross$(exeext); \
        fi
        if [ -f $(GCC_DIR)/gnatsym$(exeext) ] ; then \
          mv $(GCC_DIR)/gnatsym$(exeext) $(GCC_DIR)/gnatsym-cross$(exeext); \
        fi
    
     
  • mikepm
    mikepm
    2012-10-17

    Thanks, I missed those downloads! I 'll check out your package.

    Mike

     
  • mikepm
    mikepm
    2012-10-17

    A correction to my original post.

    The messages I got when the ada compiler wasn't working were

    i686-w64-mingw32-gcc -c helloworld.adb
    helloworld.adb:2:06: "ada" is not a predefined library unit
    helloworld.adb:2:06: "helloworld (body)" depends on "text_io (spec)"
    helloworld.adb:2:06: "text_io (spec)" depends on "ada (spec)"
    i686-w64-mingw32-gnatmake: "helloworld.adb" compilation error
    

    and not

    linux32mingw32/mingw32/bin/i686-w64-mingw32-gnatmake helloworld.adb
    i686-w64-mingw32-gcc -c helloworld.adb
    i686-w64-mingw32-gcc -c -I./ -I- /home/mike/mingw64/linux32mingw32/mingw32/lib/gcc/i686-w64-mingw32/4.7.2/adainclude/text_io.ads
    i686-w64-mingw32-gnatbind -x helloworld.ali
    i686-w64-mingw32-gnatbind: Cannot find: s-stalib.ali
    i686-w64-mingw32-gnatmake: *** bind failed.
    
     
  • Chris Sparks
    Chris Sparks
    2013-07-29

    This topic interests me. I'd like to try my hand at rebuilding the Ada compiler myself. Always fascinated me to be able to.

    What I'd really want to do is create a ARM7 based compiler for use in Android development with Ada.

    Chris

     
  • NightStrike
    NightStrike
    2013-07-31

    IIUC, there is a bootstrapping issue in that you need an ada compiler to build the ada compiler.