{$ifndef ALLPACKAGES}

{$mode objfpc}{$H+}
program fpmake;

uses
  {$ifdef unix}cthreads,{$endif} fpmkunit,
  sysutils, classes;
{$endif ALLPACKAGES}

const
  NoGDBOption: boolean = false;
  GDBMIOption: boolean = false;
  GDBMI_Disabled: boolean = false;
  LLVM_Disabled: boolean = false;
  GDBMI_DEFAULT_OSes = [aix, darwin, freebsd, haiku, linux, netbsd, openbsd, solaris, win32, win64];

procedure ide_check_gdb_availability(Sender: TObject);

  function DetectLibGDBDir: string;

  var
    SearchPath: string;

  const
    LibGDBName = 'libgdb.a';

  begin
    result:='';
    // First look for the GDBLIBDIR environment variable
    SearchPath := GetEnvironmentVariable('GDBLIBDIR');
    if (SearchPath<>'') and DirectoryExists(SearchPath) and FileExists(IncludeTrailingPathDelimiter(SearchPath)+LibGDBName) then
      begin
        result:=IncludeTrailingPathDelimiter(SearchPath);
        exit;
      end;
    // Search in the default locations
    SearchPath := '..'+PathDelim+'libgdb'+PathDelim+OSToString(Defaults.OS)+PathDelim+CPUToString(Defaults.CPU)+PathDelim;
    if DirectoryExists(SearchPath) and FileExists(SearchPath+LibGDBName) then
      begin
        result := SearchPath;
        exit;
      end;
    SearchPath := '..'+PathDelim+'libgdb'+PathDelim+OSToString(Defaults.OS)+PathDelim;
    if DirectoryExists(SearchPath) and FileExists(SearchPath+LibGDBName) then
      begin
        result := SearchPath;
        exit;
      end;
    // No custom libgdb.a found, try using system default library if available
    SearchPath := '..'+PathDelim+'lib'+PathDelim;
    if DirectoryExists(SearchPath) and FileExists(SearchPath+LibGDBName) then
      begin
        result := SearchPath;
        installer.BuildEngine.Log(vlWarning, 'Using system default libgdb file located in '+Result);
        exit;
      end;
    SearchPath := '..'+PathDelim+'usr'+PathDelim+'lib'+PathDelim;
    if DirectoryExists(SearchPath) and FileExists(SearchPath+LibGDBName) then
      begin
        result := SearchPath;
        installer.BuildEngine.Log(vlWarning, 'Using system default libgdb file located in '+Result);
        exit;
      end;
    SearchPath := '..'+PathDelim+'usr'+PathDelim+'local'+PathDelim+'lib'+PathDelim;
    if DirectoryExists(SearchPath) and FileExists(SearchPath+LibGDBName) then
      begin
        result := SearchPath;
        installer.BuildEngine.Log(vlWarning, 'Using system default libgdb file located in '+Result);
        exit;
      end;
  end;

var
  CompilerDir, GDBLibDir: string;
  P: TPackage;

  procedure maybe_regenerate_msg_files;
  var
    cmd, msgfile, msgidxfile, msgtxtfile, fpclang : string;
    Opts : TStringList;
  begin
    msgidxfile:=CompilerDir+PathDelim+'msgidx.inc';
    msgtxtfile:=CompilerDir+PathDelim+'msgtxt.inc';
    if FileExists(msgidxfile) and FileExists(msgtxtfile) then
      Exit;
    fpclang:=GetEnvironmentVariable('FPCLANG');
    if fpclang='' then
      fpclang:='e';
    msgfile:=CompilerDir+PathDelim+'msg/error'+fpclang+'.msg';
    if not FileExists(msgfile) and (fpclang<>'e') then
      begin
        fpclang:='e';
        msgfile:=CompilerDir+PathDelim+'msg/error'+fpclang+'.msg';
      end;
    if not FileExists(msgfile) then
      Exit;
    Cmd:=CompilerDir+PathDelim+AddProgramExtension('msg2inc',Defaults.BuildOS);
    if not FileExists(Cmd) then
       Cmd:=FileSearch(AddProgramExtension('msg2inc',Defaults.BuildOS),GetEnvironmentVariable('PATH'),False);
    if not FileExists(Cmd) then
      begin
        Installer.BuildEngine.log(vlWarning, 'msg2inc utility not found');
        exit;
      end;
    Opts:=TStringList.Create;
    try
      Opts.Add(msgfile);
      Opts.Add(CompilerDir+PathDelim+'msg');
      Opts.Add('msg');
      Installer.BuildEngine.log(vlCommand, 'Regenerating msg files');
      Installer.BuildEngine.ExecuteCommand(Cmd,Opts);
    finally
      Opts.Free;
    end;
  end;

begin
  P := sender as TPackage;
  with installer do
    begin
      CompilerDir:=P.Directory +'../../compiler';
      maybe_regenerate_msg_files;

    if GDBMIOption then
      begin
        BuildEngine.log(vlCommand, 'Compiling IDE with GDB/MI debugger support, LibGDB is not needed');
        P.Options.Add('-dGDBMI');
        { AIX also requires -CTsmalltoc for gdbmi }
        if Defaults.OS=aix then
          P.Options.Add('-CTsmalltoc');
      end
    else if not (NoGDBOption) then
      begin
        // Detection of GDB.
        GDBLibDir := DetectLibGDBDir;
        if GDBLibDir<>'' then
          begin
            // Include GDB
            BuildEngine.log(vlCommand, 'LibGDB was found, build IDE with debugger support');
            if FileExists(GDBLibDir+'gdblib.inc') then
              begin
                P.Options.Add('-dUSE_GDBLIBINC');
                P.Options.Add('-I'+GDBLibDir);
              end;
            P.Options.Add('-Fl'+GDBLibDir);

            case Defaults.OS of
              win32,
              win64 :   begin
                          P.Options.Add('-Xe');
                          P.Options.Add('-k--allow-multiple-definition');
                        end;
              freebsd : begin
                          P.Options.Add('-Fl/usr/local/lib');
                          P.Options.Add('-Xd');
                        end;
              openbsd : begin
                          P.Options.Add('-Fl/usr/local/lib');
                          P.Options.Add('-Fl/usr/lib');
                          P.Options.Add('-Xd');
                        end;
              netbsd  : P.Options.Add('-Xd');
              linux   : P.Options.Add('-Xd');
              aix     : begin
                          P.Options.Add('-Xd');
                          P.Options.Add('-Fl/opt/freeware/lib');
                          P.Options.Add('-k-bbigtoc');
                        end;
            else
              ; // Avoid compiler warning
            end; {case}

            P.NeedLibc := true;
          end
        else
          begin
          BuildEngine.log(vlCommand, 'LibGDB was not found, IDE has no debugger support');
          P.Options.Add('-dNODEBUG');
          end;
      end
    else
      begin
      BuildEngine.log(vlCommand, 'Debugger support disabled');
      P.Options.Add('-dNODEBUG');
      end;
    end;
end;


procedure add_ide_comandlineoptions();
begin
  AddCustomFpmakeCommandlineOption('CompilerTarget','Target CPU for the IDE''s compiler');
  AddCustomFpmakeCommandlineOption('NoGDB','If value=1 or ''Y'', no GDB support');
  AddCustomFpmakeCommandlineOption('NoGDBMI','If value=1 or ''Y'', explicitly disable GDB/MI option');
  AddCustomFpmakeCommandlineOption('GDBMI','If value=1 or ''Y'', builds IDE with GDB/MI support (no need for LibGDB)');
  AddCustomFpmakeCommandlineOption('NoIDE','If value=1 or ''Y'', the IDE will be skipped');
  AddCustomFpmakeCommandlineOption('IDE','If value=1 or ''Y'', the IDE will be build for each target');
  AddCustomFpmakeCommandlineOption('LLVM','If value=1 or ''Y'', the Compiler codegenerator will use LLVM');
  AddCustomFpmakeCommandlineOption('NoLLVM','If value=1 or ''Y'', ito explicitly disable use of LLVM');
end;

procedure add_ide(const ADirectory: string);

Var
  P : TPackage;
  T : TTarget;
  CompilerTarget : TCpu;
  CompilerDir,
  s: string;
  llvm: boolean;

begin
  if SameText(Defaults.SubTarget,'unicodertl') then
    exit;
  if Defaults.Namespaces then 
    exit;
     
  With Installer do
    begin
    s := GetCustomFpmakeCommandlineOptionValue('NoIDE');
    if (s='1') or (s='Y') then
      Exit;

    s := GetCustomFpmakeCommandlineOptionValue('NoGDB');
    if (s='1') or (s='Y') then
     NoGDBOption := true;
    s := GetCustomFpmakeCommandlineOptionValue('NOGDBMI');
    if (s='1') or (s='Y') then
     GDBMI_Disabled := true;
    if not GDBMI_Disabled then
      begin
        s := GetCustomFpmakeCommandlineOptionValue('GDBMI');
        if (s='1') or (s='Y') then
          GDBMIOption := true;
        if (Defaults.OS in GDBMI_DEFAULT_OSes) then
          GDBMIOption := True;
      end;
    s :=GetCustomFpmakeCommandlineOptionValue('CompilerTarget');
    if s <> '' then
      CompilerTarget:=StringToCPU(s)
    else
      CompilerTarget:=Defaults.CPU;
{$ifdef CPULLVM}
    llvm:=true;
{$else}
    llvm:=false;
{$endif}
    s := GetCustomFpmakeCommandlineOptionValue('NOLLVM');
    if (s='1') or (s='Y') then
     LLVM_Disabled := true;
    if LLVM_Disabled then
      llvm:=false
    else
      begin
        s:=GetCustomFpmakeCommandlineOptionValue('LLVM');
        if (s='1') or (s='Y') then
          llvm:=true;
      end;
    { Only try to build natively }
    { or for cross-compile if the resulting executable
      does not depend on C libs }
    if ((GDBMIOption or NoGDBOption) and
        ((Defaults.BuildOS=Defaults.OS) and (Defaults.BuildCPU=Defaults.CPU)
         or (Defaults.OS in [go32v2,win32,win64,linux,freebsd])
         or not Defaults.SkipCrossPrograms)) or
       { This is the list of native targets that can be compiled natively with gdbint packages }
       ((((Defaults.BuildOS=Defaults.OS) and (Defaults.BuildCPU=Defaults.CPU)) or
          not Defaults.SkipCrossPrograms) and
        (Defaults.OS in [go32v2,win32,win64,linux,freebsd,os2,emx,beos,haiku])
       ) then
      begin
        P:=AddPackage('ide');
        P.Version:='3.3.1';
{$ifdef ALLPACKAGES}
        P.Directory:=ADirectory;
{$endif ALLPACKAGES}

        s :=GetCustomFpmakeCommandlineOptionValue('IDE');
        if (s='1') or (s='Y') then
          P.OSes := AllOSes
        else
          P.OSes := AllOSes-[darwin];

        P.Dependencies.Add('rtl-extra');
        P.Dependencies.Add('fv');
        P.Dependencies.Add('chm');
        { This one is only needed if DEBUG is set }
        P.Dependencies.Add('regexpr');
        if not (NoGDBOption) and not (GDBMIOption) then
          P.Dependencies.Add('gdbint',AllOSes-AllAmigaLikeOSes);
        if GDBMIOption then
          P.Dependencies.Add('fcl-process');
        P.Dependencies.Add('graph',[go32v2]);
        P.Dependencies.Add('ami-extra',AllAmigaLikeOSes);

        P.SupportBuildModes:=[bmOneByOne];

        P.Options.Add('-Ur');
        P.Options.Add('-dNOCATCH');
        P.Options.Add('-dBrowserCol');
        P.Options.Add('-dGDB');
        if CompilerTarget=wasm32 then
          P.Options.Add('-dNOOPT');
        
        CompilerDir:=P.Directory +'../../compiler';

        P.Options.Add('-d'+CPUToString(CompilerTarget));
        P.Options.Add('-Fu'+CompilerDir);
        P.Options.Add('-Fu'+CompilerDir+'/'+CPUToString(CompilerTarget));
        P.Options.Add('-Fu'+CompilerDir+'/targets');
        P.Options.Add('-Fu'+CompilerDir+'/systems');
        P.Options.Add('-Fi'+CompilerDir+'/'+CPUToString(CompilerTarget));
        P.Options.Add('-Fi'+CompilerDir);
        
        if CompilerTarget in [x86_64, i386, i8086] then
          P.Options.Add('-Fu'+CompilerDir+'/x86');
        
        if CompilerTarget in [powerpc, powerpc64] then
          P.Options.Add('-Fu'+CompilerDir+'/ppcgen');

        if CompilerTarget in [arm, aarch64] then
          P.Options.Add('-Fu'+CompilerDir+'/armgen');

        if CompilerTarget in [sparc, sparc64] then
          begin
              P.Options.Add('-Fu'+CompilerDir+'/sparcgen');
              P.Options.add('-Fi'+CompilerDir+'/sparcgen');
          end;
        if CompilerTarget in [riscv32, riscv64] then
          begin
              P.Options.Add('-Fu'+CompilerDir+'/riscv');
          end;
        
        if CompilerTarget = mipsel then
          P.Options.Add('-Fu'+CompilerDir+'/mips');

        if CompilerTarget = loongarch64 then
          P.Options.Add('-Fu'+CompilerDir+'/loongarch64');

        if llvm then
          begin
            P.Options.Add('-Fu'+CompilerDir+'/llvm');
            P.Options.Add('-Fi'+CompilerDir+'/llvm');
          end;

        { powerpc64-aix compiled IDE needs -CTsmalltoc option }
        if (Defaults.OS=aix) and (Defaults.CPU=powerpc64) then
        P.Options.Add('-CTsmalltoc');
        
        { Handle SPECIALLINK environment variable if available }
        s:=GetEnvironmentVariable('SPECIALLINK');
        if s<>'' then
          P.Options.Add(s);
        
        P.Options.Add('-Sg');
        P.IncludePath.Add('compiler');

        T:=P.Targets.AddProgram('fp.pas');
        if CompilerTarget<>Defaults.CPU then
          begin
            T.SetExeName(AddProgramExtension(CPUToString(CompilerTarget)+'-fp',Defaults.BuildOS));
            P.SetUnitsOutputDir(P.GetUnitsOutputDir(Defaults.BuildTarget)+CPUToString(CompilerTarget));
            P.Options.Add('-dCROSSGDB');
          end;

        with T.Dependencies do
          begin
            AddUnit('compunit');
          end;

        T:=P.Targets.AddUnit('compunit.pas');
        T.Directory:='compiler';
        T.Install:=false;

        if (OSToString(defaults.OS)=lowercase({$I %FPCTARGETOS%})) and
          (CPUToString(defaults.CPU)=lowercase({$I %FPCTARGETCPU%})) then
        begin
          P.InstallFiles.Add('fp.ans','$(bininstalldir)');
          P.InstallFiles.Add('gplprog.pt','$(bininstalldir)');
          P.InstallFiles.Add('gplunit.pt','$(bininstalldir)');
          P.InstallFiles.Add('program.pt','$(bininstalldir)');
          P.InstallFiles.Add('unit.pt','$(bininstalldir)');
          P.InstallFiles.Add('cvsco.tdf','$(bininstalldir)');
          P.InstallFiles.Add('cvsdiff.tdf','$(bininstalldir)');
          P.InstallFiles.Add('cvsup.tdf','$(bininstalldir)');
          P.InstallFiles.Add('grep.tdf','$(bininstalldir)');
          P.InstallFiles.Add('tpgrep.tdf','$(bininstalldir)');
          P.InstallFiles.Add('fp32.ico', [win32, win64], '$(bininstalldir)');
        end;

        with P.Sources do
        begin
        AddDoc('readme.ide');
        AddSrc('README.txt');
        AddSrc('TODO.txt');
        AddSrc('fp.ans');
        AddSrcFiles('*.tdf',P.Directory);
        AddSrcFiles('*.pas',P.Directory,true);
        AddSrcFiles('*.inc',P.Directory,true);
        AddSrcFiles('*.rc',P.Directory);
        AddSrcFiles('*.ico',P.Directory);
        AddSrcFiles('*.term',P.Directory);
        AddSrcFiles('*.pt',P.Directory);
        end;

        P.CleanFiles.Add('$(UNITSOUTPUTDIR)ppheap.ppu');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)compiler.ppu');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)comphook.ppu');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)cpuinfo.ppu');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)browcol.ppu');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)ppheap.o');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)compiler.o');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)comphook.o');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)cpuinfo.o');
        P.CleanFiles.Add('$(UNITSOUTPUTDIR)browcol.o');

        P.BeforeCompileProc:=@ide_check_gdb_availability;

        P.NamespaceMap:='namespaces.lst';
      end;
  end;
end;

{$ifndef ALLPACKAGES}
begin
  If Assigned(Installer) and not Defaults.Namespaces then
    begin
    add_ide_comandlineoptions();
    add_ide('');
    Installer.Run;
    end;
end.
{$endif ALLPACKAGES}