#!/usr/local/bin/pike

// RNTCC, a front-end to MS VC++ with options similar to GCC
// Written by Fredrik Hubinette.

inherit "lib.pike";

// Verbose is default for now, this can be turned off one this
// frontend has been refined to where it does not require more
// debugging.

int verbose=0;

// Temporary variable
int linking_failed;

// Files to remove upon exit
string *tmpfiles=({});

void exit(int code)
{
  if(getenv("CLEANUP")!="no")
    Array.map(tmpfiles,rm);

  predef::exit(code);
}

string get_ext(string file)
{
  sscanf(file=lower_case(reverse(file)),"%s.",file);
  return reverse(file);
}

string remove_ext(string file)
{
  sscanf(file=reverse(file),"%*s.%s",file,file);
  return reverse(file);
}


int main(int argc, string *argv)
{
  string *ldopts=({"-INCREMENTAL:no"});
  string *cflags=({
    "-Zm300",
    "-F8388608",
    "-Ge",
    "-D__WIN32__",
    "-D_WIN32",
    "-D__NT__",
//    "-D__STDC__",  
    "-nologo",
  });

  string target="exe";
  int debug,optimize,share;
  string output, wantfile;

  mixed *opts=Getopt.find_all_options(argv, aggregate(
    ({"oper_pre",Getopt.NO_ARG, ({"-E"}) }),
    ({"oper_comp",Getopt.NO_ARG, ({"-c"}) }),	
    ({"verbose",Getopt.NO_ARG, ({"-v"}) }),	
    ({"debug",Getopt.MAY_HAVE_ARG, ({"-g"}) }),
    ({"optimize",Getopt.MAY_HAVE_ARG, ({"-O"}) }),
    ({"include",Getopt.HAS_ARG, ({"-I"}) }),
    ({"link",Getopt.HAS_ARG, ({"-l"}) }),
    ({"share",Getopt.MAY_HAVE_ARG, ({"-s"}) }),
    ({"ignore",Getopt.MAY_HAVE_ARG, ({"-t"}) }),
    ({"ignore",Getopt.HAS_ARG, ({"-R","-L","-r"}) }),
    ({"warn",Getopt.MAY_HAVE_ARG, ({"-W"}) }),
    ({"define",Getopt.HAS_ARG, ({"-D"}) }),
    ({"undefine",Getopt.HAS_ARG, ({"-U"})}),
    ({"output",Getopt.HAS_ARG, ({"-o"}) }),
    ({"export",Getopt.HAS_ARG, ({"--export"}) })
    ));
  foreach(opts, mixed *option)
    {
      switch(option[0])
      {
	case "verbose":
	  verbose++;
	  ldopts+=({"-VERBOSE:lib"});
	  break;

	case "export": // fixme
//	  ldopts+=({"export",option[1]+"_"});
	  break;

	case "share": // fixme
	  switch(option[1])
	  {
	    case "hare":
	      share=1;
	      target="dll";
	      break;

	    case "tatic":
	      /* Not yet implemented */
	      break;
	  }
	  break;
	  
	case "oper_pre":
	  cflags+=({ "-E" });
	  target="-";
	  break;

	case "oper_comp":
	  target="obj";
	  cflags+=({ "-c" });
	  break;

	case "debug":
	  cflags+=({
	    "-Z7",
//	    "-Yd",
	  });
	  debug=1;
	  break;
	  
	case "optimize":
	  if(!option[1]) option[1]=1;
	  switch(optimize=(int)option[1])
	  {
	    case 0: optimize=0; break;
	    case 1: cflags+=({"-O1"}); break;
	    case 2: cflags+=({"-Ob2gity","-GB"}); break;
	    case 3..:
	      break;
	  }
	  break;
	  
	case "include":
	  // Avoid searching 'local' include dirs.
	  // This is not a very pretty solution.
	  if(sscanf(option[1],"/usr/include/%*s") ||
	     sscanf(option[1],"/usr/local/%*s"))
	    break;
	  
	  cflags+=({"-I"+fixpath(option[1])});
	  break;
	  
	case "link":
	  // -lm and -lc are automatically handled ?
	  if(option[1]=="m" || option[1]=="c") break;

	  // We optimiza a little, no need to bring in the same
	  // library many times in a row.
//	  if(cflags[-1]!=option[1]+".lib")
//	  if(search(cflags,option[1]+".lib")!=-1)
	    cflags-=({option[1]+".lib"});
	    cflags+=({option[1]+".lib"});
	  break;
	  
        case "warn":
	  if(option[1])
	  {
	    // This allows us to pass options to the linker
	    if(sscanf(option[1],"l,%s",string tmp))
	    {
	      // This was done for my convenience, it can be taken
	      // out once smartlink has been fixed to not use absoute
	      // paths for the 'uname' binary.
	      if(sscanf(tmp,"-rpath%*s")) break;
	      
	      cflags+=({tmp});
	      break;
	    }
	  }

	  // More options should be recognized, options which are not
	  // recognized should generate warning/error messages.
	  switch(option[1])
	  {
	    case "all": cflags+=({"-W3"}); break;
	    default: cflags+=({"-W2"}); break;
	  }
	  break;
	  
	case "define": cflags+=({"-D"+option[1]}); break;
	case "undefine": cflags+=({"-U"+option[1]}); break;
	case "output":
	  output=option[1];
	  break;
      }
    }

  // Scan through the remaining arguments
  argv=Getopt.get_args(argv);
  cflags+=Array.map(argv[1..],fixpath);

  foreach(argv[1..], string tmp)
  {
    if(tmp[0]=='-')
    {
      werror("Unrecognized option "+tmp+".\n");
      exit(1);
    }
  }

  switch(target)
  {
    case "exe":
      if(!output) output="a.out";
      cflags+=({"-Fe"+fixpath(output)+".exe"});
//      cflags+=({"-MT" + (debug?"d":"") });
      wantfile=output+".exe";
      break;

    case "obj":
      if(!output)
	output=remove_ext(argv[1])+".o";
      cflags+=({"-Fo"+fixpath(output)});
      wantfile=output;
      break;

    case "dll":
      if(output)
	cflags+=({"-Fe"+fixpath(output)});
      else
	output=remove_ext(argv[1])+".dll";

//      cflags+=({"-MD" + (debug?"d":"") });
      wantfile=output;
      break;

    case "-":
  }


  cflags+=({"-MT" + (debug?"d":"") });

  if(output) rm(output);

  array(string) cmd=({"cl"})+cflags;

  switch(target)
  {
    case "exe":
    case "dll":
      if(debug)
      {
	array libs=({"libc.lib","libcd.lib",
		     "libcmt.lib","libcmtd.lib",
		     "msvcrt.lib","msvcrtd.lib"});

	string lib=(debug?"libcmtd.lib":"libcmt.lib");
	libs-=({ lib });
	for(int e=0;e<sizeof(libs);e++)
	  ldopts+=({"-nodefaultlib:"+libs[e]});

	
	cmd+=({"-link",
//	  "-PDB:NONE",
	  "-DEBUGTYPE:BOTH",
	  "-DEBUG",
	  })
	  +ldopts;
      }
  }


  int ret;
  if(verbose && target!="-")
  { 
    ret=do_cmd(cmd);
  }else{
    int first_line=1;
    // cl.exe echoes the file name of the file we are compiling
    // first, we need to get rid of that to make configure behave 
    ret=silent_do_cmd(cmd, lambda(string line) 
		      {
			if(first_line)
			{
			  array x=line/"\n";
			  if(sizeof(x)>1)
			  {
			    line=x[1..]*"\n";
			    first_line=0;
			  }else{
			    return;
			  }
			}
			write(line);
		      },1);
  }

  if(ret)
  {
    werror("CL returned error code %d.\n",ret);
    exit(ret);
  }

  if(wantfile)
  {
    if(!file_stat(wantfile))
    {
      werror("RNTCL: output file not generated (%s).\n",wantfile);
      exit(1);
    }
  }

  if(target=="exe" && !share)
  {
    rm(output);
    Stdio.write_file(output,
		     "#!/usr/local/bin/pike\n"
		     "inherit \""+find_lib_location()+"\";\n"
		     "int main(int argc, string *argv) {\n"
		     "argv[0]+=\".exe\";\n"
		     "argv[0]=getenv(\"NTDRIVE\")+fixpath(combine_path(getcwd(),argv[0]));\n"
		     "  int ret=silent_do_cmd(argv);\n"
		     "  exit(ret);\n"
		     "}\n");
    chmod(output,0755);
  }

  exit(ret);
}
