Porting make.c to D

Walter Bright is the "benevolent lifelong dictator" of the D programming language and founder of Digital Mars . He has more than a dozen years of experience in developing compilers and interpreters for several languages, including Zortech C ++, the first native C ++ compiler. He is also the creator of Empire , the main inspiration for Sid Meier's Civilization.



Better C Series
  1. D C
  2. ,
  3. make.c D


Better C is a way to port existing C projects to D in a consistent manner. This article shows you a step-by-step process for converting a non-trivial project from C to D and shows you the common problems that arise.



Although the frontend of the D compiler dmd has already been converted to D, it is such a large project that it is difficult to cover it entirely. I needed a smaller and more modest project that could be fully understood, but that it was not a speculative example.



An old make program that I wrote for the Datalight C compiler in the early 1980s came to my mind . This is a real implementation of the classic make program, which has been in constant use since the early 80's. It was written in C even before it was standardized, was ported from one system to another and fits in just 1961 lines of code, including comments. It is still used regularly today.



Here is the documentation and source code . The size of the make.exe executable is 49,692 bytes and was last modified on August 19, 2012.



Our Evil Plan:



  1. Minimize diffs between C and D versions. Thus, if the behavior of programs differs, it will be easier to find the source of the difference.
  2. C. โ„– 1.
  3. . , . โ„– 1.
  4. C , .
  5. , โ„– 4.


, , , ..



!



C D. โ€” 52โ€‰252 ( โ€” 49โ€‰692 ). , , - NEWOBJ ( C- ) DMC 2012 .





. 664 1961, โ€” , , , , .



#include D: , #include <stdio.h> import core.stdc.stdio;. , Digital Mars C, D ( ). , 29- 64-. (. import).



#if _WIN32 version (Windows). (. ).



extern(C): C. (. ).



debug1, debug2 debug3 debug prinf. , #ifdef DEBUG debug. (. debug).



/* Delete these old C macro definitions...
#ifdef DEBUG
-#define debug1(a)       printf(a)
-#define debug2(a,b)     printf(a,b)
-#define debug3(a,b,c)   printf(a,b,c)
-#else
-#define debug1(a)
-#define debug2(a,b)
-#define debug3(a,b,c)
-#endif
*/

// And replace their usage with the debug statement
// debug2("Returning x%lx\n",datetime);
debug printf("Returning x%lx\n",datetime);


TRUE, FALSE NULL true, false null.



ESC . (. ).



// #define ESC     '!'
enum ESC =      '!';


NEWOBJ .



// #define NEWOBJ(type)    ((type *) mem_calloc(sizeof(type)))
type* NEWOBJ(type)() { return cast(type*) mem_calloc(type.sizeof); }


filenamecmp .



.



D (thread-local storage, TLS). make โ€” , __gshared. (. __gshared).



// int CMDLINELEN;
__gshared int CMDLINELEN


D , typedef . alias. (. alias). , struct.



/*
typedef struct FILENODE
        {       char            *name,genext[EXTMAX+1];
                char            dblcln;
                char            expanding;
                time_t          time;
                filelist        *dep;
                struct RULE     *frule;
                struct FILENODE *next;
        } filenode;
*/
struct FILENODE
{
        char            *name;
        char[EXTMAX1]  genext;
        char            dblcln;
        char            expanding;
        time_t          time;
        filelist        *dep;
        RULE            *frule;
        FILENODE        *next;
}

alias filenode = FILENODE;


D macro โ€” , MACRO.



C, D , :



// char *name,*text;
// In D, the * is part of the type and 
// applies to each symbol in the declaration.
char* name, text;


C D. (. D).



static D . C D, , . __gshared, . (. static).



/*
static ignore_errors = FALSE;
static execute = TRUE;
static gag = FALSE;
static touchem = FALSE;
static debug = FALSE;
static list_lines = FALSE;
static usebuiltin = TRUE;
static print = FALSE;
...
*/

__gshared
{
    bool ignore_errors = false;
    bool execute = true;
    bool gag = false;
    bool touchem = false;
    bool xdebug = false;
    bool list_lines = false;
    bool usebuiltin = true;
    bool print = false;
    ...
}


D . , , , .



make- .



, , , D .



// int cdecl main(int argc,char *argv[])
int main(int argc,char** argv)


mem_init() , .



C , D , .



void cmderr(const char* format, const char* arg) {...}

// cmderr("can't expand response file\n");
cmderr("can't expand response file\n", null);


- (->) C (.), D .



version.



/*
 #if TERMCODE
    ...
 #endif
*/
    version (TERMCODE)
    {
        ...
    }


. D .



// doswitch(p)
// char *p;
void doswitch(char* p)


D debug . xdebug.



C \n\ . D .



/+ +/. (. , ).



static if #if. (. static if).



D , .ptr.



// utime(name,timep);
utime(name,timep.ptr);


const C D, D . (. const immutable).



// linelist **readmakefile(char *makefile,linelist **rl)
linelist **readmakefile(const char *makefile,linelist **rl)


void* char* D .



// buf = mem_realloc(buf,bufmax);
buf = cast(char*)mem_realloc(buf,bufmax);


unsigned uint.



inout , ยซยป . const, , . (. inout-).



// char *skipspace(p) {...}
inout(char) *skipspace(inout(char)* p) {...}


arraysize .length. (. ).



// useCOMMAND  |= inarray(p,builtin,arraysize(builtin));
useCOMMAND  |= inarray(p,builtin.ptr,builtin.length)


(immutable), , . (. ).



// static char envname[] = "@_CMDLINE";
char[10] envname = "@_CMDLINE";


.sizeof sizeof() C. (. .sizeof).



// q = (char *) mem_calloc(sizeof(envname) + len);
q = cast(char *) mem_calloc(envname.sizeof + len)


Windows .



char * void*.



! , . , , โ€” , .



man.c, , make -man. , D, .



make , make-:



\dmd2.079\windows\bin\dmd make.d dman.d -O -release -betterC -I. -I\dmd2.079\src\druntime\import\ shell32.lib




C D, . .



, , :



  • #include import;
  • D- ;
  • ->;
  • :

    • ,
    • ,
    • ,
    • ,
    • ;
  • ;
  • ;
  • ;
  • ;
  • ;
  • C D.


:



  • ,
  • ,
  • ,
  • ,
  • .




, Better C, , :





To action



If you know English, head over to Forum D and tell us how your Better C project is progressing!




All Articles