rakelib/setup.rake
author Jan Vrany <jan.vrany@labware.com>
Wed, 04 Nov 2020 11:10:18 +0000
changeset 310 2cf08578aa5f
parent 309 6e39986dfabb
child 334 eb15c224410b
permissions -rw-r--r--
Do not ship VDB as part of default build / toy archive The reason is that few people used it and those who did (or will) are those who may want to hack on it. To make this easier when using toy archive, we won't ship VDB in toy archive anymore and instead we'll document on how to install it in VDB's `README.md`

unless defined? BUILD_DIR;     BUILD_DIR = 'build'; end
unless defined? BUILD_ID;      BUILD_ID = (ENV['BUILD_NUMBER'] ? "build#{ENV['BUILD_NUMBER']}" : "#{Time.now.strftime('%Y%m%d')}"); end
unless defined? ARTIFACTS_DIR; ARTIFACTS_DIR = 'artifacts'; end
unless defined? REPORT_DIR;    REPORT_DIR = 'reports'; end
unless defined? TMP_DIR;       TMP_DIR = 'tmp'; end
unless defined? DEBUG;         DEBUG = nil; end

load BUILD_DIR / 'stx-config.rake' if File.exist?(BUILD_DIR / 'stx-config.rake')

unless defined? PROJECT;       PROJECT = 'stx:jv-branch'; end



# Guess the value of BUILD_TARGET for system on which we're currently 
# running in form of GNU target triplet, such as `x86_64-pc-linux-gnu`.
# Essentially a wrapper for config.guess used with autotools. 
def build_target_guess
  target = `sh config.guess`.chop
  # Workaround for MSYS2 / MINGW environment. When using MSYS2 environment to build 
  # Smalltalk/X, GNU config.guess reports `x86_64-pc-msys` rather than `i686-pc-mingw32`
  # or `x86_64-w64-mingw32` which are the values used by CI and/or third-party libraries. 
  # Therefore we unify the value here (at least for now).
  case target
    when 'x86_64-pc-msys'
      target = 'x86_64-w64-mingw32'
  end  
  # /Workaround
  return target
end

if not defined? BUILD_TARGET;
  BUILD_TARGET = ENV['BUILD_TARGET'] || build_target_guess;
end
ENV['BUILD_TARGET'] = BUILD_TARGET

# "Guess" the current "branch".
def build_branch_guess()
  repo =  HG::Repository.new('.')
  return repo.bookmark() || repo.branch()
end

BUILD_BRANCH = build_branch_guess()

unless defined? TOOLCHAIN
  if ENV.has_key? 'TOOLCHAIN'
    TOOLCHAIN = ENV['TOOLCHAIN']
  else
    if unix?
      TOOLCHAIN='gcc'
    elsif win32?    
      if /x86_64/ =~ BUILD_TARGET
        TOOLCHAIN='mingw64'
      else
        TOOLCHAIN='mingw32'
      end
    else
      raise Exception.new('Unsupported operating system')
    end
  end
end

unless File.exist? BUILD_DIR / 'stx-config.rake'
  if defined? PROJECT
    mkdir_p BUILD_DIR
    File.open(BUILD_DIR / 'stx-config.rake', 'w') do | f |
      f.puts <<COMMENT
# This file contans a default build configuration. It is created automatically
# when `rake` is called for the very first time. You may still override values
# on command line, i.e., 
#
#     rake PROJECT=some-other-project 
#
# This file SHOULD NOT be checked in a repository. 
#
COMMENT
      f.puts "unless defined? PROJECT; PROJECT='#{PROJECT}'; end"
      f.puts "unless defined? BUILD_TARGET; BUILD_TARGET = '#{BUILD_TARGET}'; end"      
      if defined? PROJECT_DIRS
        f.puts "unless defined? PROJECT_DIRS; PROJECT_DIRS='#{PROJECT_DIRS}'; end"
      end
    end
  end
end

unless defined? PROJECT_DIRS; PROJECT_DIRS = ''; end

case TOOLCHAIN
  when 'gcc'
    OBJ_DIR = '.'
    OBJ_SUFFIX = 'o'
    MAKE=ENV['MAKE'] || 'make'

  when 'bcc'
    OBJ_DIR = 'objbc'
    OBJ_SUFFIX = 'obj'
    MAKE='bmake.bat -DUSEBC'

  when 'mingw64'
    if File.exist? 'C:\mingw64\bin'
      ENV['MINGW_DIR'] ='C:\MINGW64'
      ENV['USEMINGW_ARG'] = '-DUSEMINGW64'
    elsif File.exist? 'C:\MSYS64\MINGW64\bin\gcc.exe'
      ENV['MINGW_DIR'] ='C:\MSYS64\MINGW64'
      ENV['USEMINGW_ARG'] = '-DUSEMINGW64'
    else
      raise Exception.new(%q{MINGW64 nor MSYS2 found in C:\MinGW64 nor C:\MSYS64!})
    end
    ENV['MINGW'] = '__MINGW64__'
    OBJ_DIR = 'objmingw'
    OBJ_SUFFIX = 'obj'
    MAKE='mingwmake.bat'
    ENV['PATH'] = "#{ENV['PATH']};#{ENV['MINGW_DIR']}\\bin"

  when 'mingw32'
    if File.exist? 'C:\mingw\bin'
      ENV['MINGW_DIR'] ='C:\MINGW'
      ENV['USEMINGW_ARG'] = '-DUSEMINGW32'
    elsif File.exist? 'C:\MSYS64\MINGW32\bin\gcc.exe'
      ENV['MINGW_DIR'] ='C:\MSYS64\MINGW32'
      ENV['USEMINGW_ARG'] = '-DUSEMINGW32'
      system 'set'
    elsif File.exist? 'C:\MSYS\MINGW32\bin\gcc.exe'
      ENV['MINGW_DIR'] ='C:\MSYS\MINGW32'
      ENV['USEMINGW_ARG'] = '-DUSEMINGW32'
    else
      raise Exception.new(%q{MINGW32 nor MSYS2 found in C:\MinGW nor C:\MSYS64 nor C:\MSYS!})
    end
    ENV['MINGW'] = '__MINGW32__'
    OBJ_DIR = 'objmingw'
    OBJ_SUFFIX = 'obj'
    MAKE='mingwmake.bat'
    ENV['PATH'] = "#{ENV['PATH']};#{ENV['MINGW_DIR']}\\bin"

  else
    raise Exception.new("Unsupported toolchain: #{TOOLCHAIN}")
end

task :setup => %i(setup:pre
                  setup:main
                  setup:post)

task :'setup:pre'
task :'setup:post'

task :'setup:main' => %i(setup:projects setup:project setup:tasks)

task :'setup:projects' do
  Rake::Stx::Configuration.read_specs(PROJECT_DIRS)
end

task :'setup:project' => :'setup:projects' do
  unless defined? PROJECT; error('PROJECT variable not defined!') end
  project! PROJECT.to_sym
  project.apply_imports
  app_name = project.app_name || 'smalltalkx'
  app_version = project.app_version || '8.0.0'

  if not defined? BUILD_NAME; 
    BUILD_NAME = "#{app_name}-#{app_version}_#{BUILD_TARGET}"    
  end  
end

task :'setup:dependencies' => :checkout do
  project.packages.each do |pkg|
    all = project.packages.collect { | pkg | pkg.name }
    if pkg.name == 'stx:stc' or pkg.name == 'stx:librun' or File.exist? (BUILD_DIR / pkg.directory / pkg.project_definition_file_name)
      unless pkg.test_package?
        prereqs_reqd = pkg.prereqs(mandatory: true)
        prereqs_refd = pkg.prereqs(referenced: true)

        # Now, check that all mandatory and required packages are defined.
        # This is merely a check to make sure we have all the code ready.
        prereqs_reqd.each do | dep |
          if not all.include? dep then
            error("Package #{pkg.name} requires #{dep} but it is not defined in spec!")
          end
        end
        prereqs_refd.each do | dep |
          if not all.include? dep then
            warn("Package #{pkg.name} references #{dep} but it is not defined in spec!")
          end
        end

        task "#{project.application.name}:dependencies" => pkg.name
        task "#{pkg.name}:dependencies" => prereqs_reqd
      end
    end
  end
end

task :'setup:tasks' => :'setup:tasks:internal'

task :'setup:tasks:internal' => :'setup:project' do

  project.tasks.each {|t| t.define!}

  # compile task
  project.application ? (task :'compile:application' => project.application.name) : (task :'compile:application')

  info 'Defining tasks for packages...'

  project.packages_and_application.each do |pkg|

    info "Defining task for package #{pkg.name}"

    if unix? #and not File.exist? BUILD_DIR / pkg.directory / 'makefile'    
      task pkg.name => [BUILD_DIR / pkg.directory / 'makefile']
      file BUILD_DIR / pkg.directory / 'makefile' do
        chdir BUILD_DIR / pkg.directory do
          raise Exception.new("Cannot run stmkmf for #{pkg.directory}") unless system "#{pkg.top / 'stx' / 'rules' / 'stmkmf'}"
        end
      end
    end

    #require 'find'
    #[ 'bitmaps', 'resources' ].each do | dir |
    #  path = BUILD_DIR / pkg.directory() / dir
    #  if File.exist?(path)
    #    Find.find(File.expand_path(path)) do | file |
    #      if FileTest.directory?(file)
    #        if (File.basename(file) == '..') || (File.basename(file) == '.')
    #          Find.prune       # Don't look any further into this directory.
    #        else
    #          if File.exists?(File.join(file, '.svn'))
    #            rm_rf File.join(file, '.svn')
    #            #Find.prune
    #          end
    #        end
    #      end
    #    end
    #  end
    #end

    task "#{pkg.name}" => [ :'setup:dependencies' , "#{pkg.name}:dependencies", "#{pkg.name}:pre", "#{pkg.name}:main", "#{pkg.name}:post" ]

    # For Smalltalk class libraries, we have to update VM symbol database
    # if changed...
    if pkg.name != 'stx:stc' && pkg.name != 'stx:librun'
      task "#{pkg.name}" => %w(stx:librun:symbols)
    end

    task "#{pkg.name}:dependencies"
    task "#{pkg.name}:pre"
    task "#{pkg.name}:post"

    task "#{pkg.name}:main" do
      make_vars = ''
      d = win32? ? '-D' : ''
      q = win32? ? '"' : "'"


      (make_vars += "STC=#{STC}") if unix?

      if pkg.application? and !win32?
        liblist=''
        libobjs=''
        link_libobjs=''
        project.packages.each do |p|
          if File.exist? (BUILD_DIR / p.directory / p.project_definition_file_name)
            if !p.application? and p.link and !p.test_package?
              liblist += "#{p.dll_name_without_suffix} "
              libobjs += "#{File.join(pkg.top, p.directory, p.dll_name)} "
              link_libobjs += "#{p.dll_name} "
            end
          end
        end
        make_vars += " #{q}#{d}LIBLIST=#{liblist}#{q}"
        make_vars += " #{q}#{d}LIBOBJS=#{libobjs}#{q}"
        make_vars += " #{q}#{d}LINK_LIBOBJS=#{link_libobjs}#{q}"
      end

      # Update stc flags,
      stc_flags = pkg.stc_flags

      (make_vars += " #{q}#{d}STC_CMD_OPT=#{stc_flags}#{q}") if stc_flags

      (rm BUILD_DIR / pkg.directory / 'GNUmakefile') if File.exist? BUILD_DIR / pkg.directory / 'GNUmakefile'

      info "Compiling #{pkg.name}..."
      make_vars = "#{make_vars} #{DEBUG}"
      chdir BUILD_DIR / pkg.directory do
        if pkg.application? and win32?
          make 'exe'
          make 'RESOURCEFILES'
        else
          if pkg.name == 'stx:stc'
            # Sigh, on UNIX makefile is generated. So if we're using
            # binary stc, we have to recreate it first.
            if unix?
              (sh "'#{STX_TOP_DIR / 'rules' / 'stmkmf'}'") unless File.exist?('makefile')
            end
            make
          elsif pkg.name == 'stx:librun'
             # A workaround for Windows 10 & ancient Borland make which 
            # tend to crash there when trying to recompile already compiled
            # librun. Sigh, we have to move away from it as soon as possible!
            (touch 'stxmain.c') if win32? and File.exist?('stxmain.c')
            make
            if !pkg.application? and win32?                            
              cp 'objmingw' / 'librun.dll',
                 pkg.top /  project.application.directory 
            end
          else
            # Somewhat stupid detection whether we run recent St/X or not
            # ('recent' mean more recent than 3 years or so)
            # TODO: Remove this hack
            if File.exist? BUILD_DIR / 'stx' / 'libbasic2' / 'Makefile.init'
              make "#{make_vars} full"
            else
              make "#{make_vars}"
            end
            # On Windows, copy the DLL application directory
            if !pkg.application? and win32?
              if !uptodate? pkg.top / project.application.directory, [ OBJ_DIR / pkg.dll_name ]
                cp OBJ_DIR / pkg.dll_name, pkg.top /  project.application.directory                
              end
              if !uptodate? pkg.top / project.application.directory, [ OBJ_DIR / "#{pkg.dll_name}.debug" ]
                cp OBJ_DIR / "#{pkg.dll_name}.debug", pkg.top /  project.application.directory                
              end
            end
          end
        end
      end

      if pkg.application? and unix?
        chdir BUILD_DIR / 'stx' / 'librun' do; make; end
      end


    end

    if pkg.application?
      if win32?
        task pkg.name => BUILD_DIR / pkg.directory / 'modules.stx'

        task BUILD_DIR / pkg.directory / 'modules.stx' do |t|
          rm t.name if File.exist? t.name
          File.open(t.name, 'w') do |f|
            project.packages.each do |p|
              if !p.application? and p.link and !p.test_package? and p.name != 'stx:stc' and p.name != 'stx:librun' and File.exist? BUILD_DIR / p.directory / OBJ_DIR / p.dll_name
                f.puts p.dll_name_without_suffix
              end
            end
          end
        end

        project.packages.each do |p|
          if !p.application? and !p.test_package?
            task pkg.name => BUILD_DIR / pkg.directory / p.dll_name

            file BUILD_DIR / pkg.directory / p.dll_name do
              if !p.application? and p.link and !p.test_package? and File.exist? BUILD_DIR / p.directory / OBJ_DIR / p.dll_name
                cp BUILD_DIR / p.directory / OBJ_DIR / p.dll_name,
                   BUILD_DIR / pkg.directory / p.dll_name
              end
            end
          end

          if win32_wine?
            file BUILD_DIR / pkg.directory / p.dll_name do
              cp BUILD_DIR / p.directory / OBJ_DIR / p.dll_name,
                 BUILD_DIR / pkg.directory / p.dll_name
            end
          end

        end
      else # UNIX
        task "#{pkg.name}:main" => BUILD_DIR / pkg.directory / 'modulList.stc'

        task BUILD_DIR / pkg.directory / 'modulList.stc' do |t|
          rm t.name if File.exist? t.name
        end

        task "#{pkg.name}:main" => BUILD_DIR / pkg.directory / 'modulList.c'

        task BUILD_DIR / pkg.directory / 'modulList.c' do |t|
          rm t.name if File.exist? t.name
        end
      end
    end
  end

end