March 28, 2024, 01:57:07 PM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


command line compiling and syntax error handling

Started by Todd Riggins, September 04, 2007, 05:01:41 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Todd Riggins

for the life of me, I could of sworn I saw a post somewhere in the ether that showed how to compile at command line and showed what the different flag options do. I searched here , but my eyes are starting to hurt to find it.

can anybody help me with this info or provide the link to the post? thanks.

Also, I would want to know briefly how to capture source code syntax errors at commandline in my own application.

Yes, I want to do my own editor. But, this editor is not the norm.

Again thanks for any help on this topic...  ;D
Brought to you buy: http://www.exodev.com

Ionic Wind Support Team

The compiler uses STDIO for output so it depends on what language you are using.

For the Aurora IDE a pipe is used and the compiler sends it's text to this pipe which is read like a file. 

The compiler only has a few options.  To just compile an Aurora .src file to assembly it would be:

acparse in.src out.asm

The compiler also supports response files, although I haven't used them much myself as you will run into problems with nasm when the input file gets too large.  A response file is just a text file where each input source file is listed per line.

acparse response.txt out.asm /r

You can also enable debugging information with /dDEBUG

/d is to define a conditional.  So if you have a #ifdef SHAREWARE in your program you could invoke the compiler with:

acparse myapp.src myapp.asm /dSHAREWARE

Paul.
Ionic Wind Support Team

Todd Riggins

Thanks paul... that should definetly get me started. This gonna be interesting... :)
Brought to you buy: http://www.exodev.com

Ionic Wind Support Team

Ionic Wind Support Team

Todd Riggins

Oh ok... I was thinking I would have to do something like

acparse in.src out.asm > errors.txt

So I need to figure out how to set my application to have a pipe where acparse can pipe text into. Sure, I could use some help! ;D thanks

...I'll still try to look up info on pipes to do what you say.

Brought to you buy: http://www.exodev.com

Ionic Wind Support Team

You could probably use a redirection like that, but you would have a hard time reading it at the correct times.

The method I use in the IDE's might be a little complex looking at first.  It is C++ code, but you should be able to follow it.  I have a class called "Process" that creates a new process in a thread, registers a few private messages, creates the pipe,and returns a status code.

The worker thread listens to the pipe and when it receives data sends a message to the main IDE window, using those registered messages, that some text is ready to display.  The IDE then updates the build window and goes on its merry way. 

All of this is to keep the program interactive while the compiler is running.

The first function called "InvokeTool" creates an instance of the Process class, lets it run, and processes a message loop for the rest of the IDE while it is running.


int CAuroraApp::InvokeTool(CString &tool, CString &params, CString &path)
{
m_bProcessComplete = FALSE;
MSG msg;
Process *oProcess = new Process(tool + " " + params,path,m_pMainWnd->GetSafeHwnd());
HANDLE hProcess = oProcess->run();
if(!hProcess)
return 1;
while(WaitForSingleObject(hProcess,0) == WAIT_TIMEOUT)
{
BOOL bIdle = TRUE;
LONG lIdleCount = 0;

while (bIdle &&
!::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}

while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
if ( !AfxGetApp()->PumpMessage( ) )
{
  ::PostQuitMessage( 0);
return (0);
}
}
}
while(!m_bProcessComplete)
{
while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
if ( !AfxGetApp()->PumpMessage( ) )
{
  ::PostQuitMessage( 0);
return (0);
}
}
}
GetExitCodeProcess(hProcess,&m_wReturnCode);
::CloseHandle(hProcess);
return m_wReturnCode;

}



And the Process class:


/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
*  2-Sep-01 | Created
*****************************************************************************/

#include "stdafx.h"
#include "process.h"

#define dim(x) (sizeof(x) / sizeof((x)[0]))

IMPLEMENT_MSG(UPM_FINISHED)
IMPLEMENT_MSG(UPM_LINE)
IMPLEMENT_MSG(UPM_PROCESS_HANDLE)

/****************************************************************************
*                                Process::run
* Result: BOOL
*       TRUE if the process started successfully
* FALSE if there was an error (use ::GetLastError to get reason)
* Effect:
*       Creates a child process, redirects stdout/stderr to the output
* stream. Does not redirect stdin.
****************************************************************************/

HANDLE Process::run()
    {
     hreadFromChild = NULL;
     HANDLE hwriteToParent = NULL;
     HANDLE hwriteToParent2 = NULL;
HANDLE hReadTemp = NULL;
     SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; // inheritable handle

     if(!::CreatePipe(&hReadTemp, &hwriteToParent, &sa, 0))
{ /* pipe failed */
// ::GetLastError() will reveal the cause
delete this;
return 0;
} /* pipe failed */

     if(!::DuplicateHandle(GetCurrentProcess(),     // duplicate from this process
   hwriteToParent,     // this handle
   GetCurrentProcess(),     // into this process
   &hwriteToParent2,        // as this handle
   0,     // no access flags (subsumed by DUPLICATE_SAME_ACCESS)
   TRUE,             // create inheritable
   DUPLICATE_SAME_ACCESS))  // create duplicate access
{ /* duplicate failed */
DWORD err = ::GetLastError();
::CloseHandle(hreadFromChild);
::CloseHandle(hwriteToParent);
::SetLastError(err);
delete this;
return 0;
} /* duplicate failed */

     STARTUPINFO startup;
     PROCESS_INFORMATION procinfo;

     ::ZeroMemory(&startup, sizeof(startup));

     startup.cb = sizeof(startup);
     startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
     startup.wShowWindow = SW_HIDE; // hidden console window
     startup.hStdInput = NULL; // not used
     startup.hStdOutput = hwriteToParent;
     startup.hStdError = hwriteToParent2;

     // We want a non-inherited read handle. DuplicateHandle with a
     // NULL target fixes the read side to be non-inheritable
     ::DuplicateHandle(::GetCurrentProcess(),    // in this process
       hReadTemp,           // child read handle
       ::GetCurrentProcess(),    // to this process
       &hreadFromChild,                     // modify existing handle
       0,                        // flags
       FALSE,                    // not inheritable
       DUPLICATE_SAME_ACCESS);   // same handle access

::CloseHandle(hReadTemp);
     // We need a writeable buffer for the command (silly Windows restriction)
     LPTSTR cmd = command.GetBuffer(command.GetLength() + 1);

     BOOL started = ::CreateProcess(NULL,        // command is part of input string
    cmd,         // (writeable) command string
    NULL,        // process security
    NULL,        // thread security
    TRUE,        // inherit handles flag
    0,           // flags
    NULL,        // inherit environment
    m_sDirectory,        // inherit directory
    &startup,    // STARTUPINFO
    &procinfo);  // PROCESS_INFORMATION
     command.ReleaseBuffer();

     if(!started)
{ /* failed to start */
DWORD err = ::GetLastError(); // preserve across CloseHandle calls
::CloseHandle(hreadFromChild);
::CloseHandle(hwriteToParent);
::CloseHandle(hwriteToParent2);
::SetLastError(err);
::PostMessage(target,UPM_FINISHED, (WPARAM)err, (LPARAM)pid);
delete this;
return FALSE;
} /* failed to start */

     //target->PostMessage(UPM_PROCESS_HANDLE, (WPARAM)procinfo.hProcess, (LPARAM)pid);
     ::CloseHandle(procinfo.hThread);
     // Now close the output pipes so we get true EOF/broken pipe
     ::CloseHandle(hwriteToParent);
     ::CloseHandle(hwriteToParent2);
     
     // We have to create a listener thread. We create a worker
     // thread that handles this
     CWinThread * thread = AfxBeginThread(listener, (LPVOID)this);
     if(thread == NULL)
{ /* failed */
DWORD err = ::GetLastError();
::PostMessage(target,UPM_FINISHED, (WPARAM)err, (LPARAM)pid);
::CloseHandle(hreadFromChild);
::CloseHandle(hwriteToParent);
::CloseHandle(hwriteToParent2);
delete this;
return 0;
} /* failed */
     return procinfo.hProcess;
    } // Process::run

/****************************************************************************
*                             Process::listener
* Inputs:
*       LPVOID me: (LPVOID)(CProcess *)
* Result: UINT
*       0, always
* Effect:
*       Maps back to the C++ space to run the listener thread
****************************************************************************/

UINT Process::listener(LPVOID me)
    {
AfxGetThread()->m_bAutoDelete = TRUE;
     ((Process *)me)->listener();
     return 0;
    } // Process::listener

/****************************************************************************
*                             Process::listener
* Result: void
*       
* Effect:
*       Reads input lines from the child process
****************************************************************************/
#define MAX_LINE_LENGTH 1024

void Process::listener()
    {
     TCHAR buffer[MAX_LINE_LENGTH + 1];

     //CString * line;
     //line = new CString;
char *line;
line = (char *)calloc(1024,1);
     DWORD bytesRead;
     
     while(::ReadFile(hreadFromChild, buffer, sizeof(buffer) - sizeof(TCHAR), &bytesRead, NULL))
{ /* got data */
if(bytesRead == 0)
    break; // EOF condition

buffer[bytesRead] = _T('\0');
// Convert to lines
LPTSTR b = buffer;
while(TRUE)
    { /* convert and send */
     LPTSTR p = _tcschr(b, _T('\n'));
     if(p == NULL)
{ /* incomplete line */
//*line += b;
strcat(line,b);
break; // leave assembly loop
} /* incomplete line */
     else
{ /* complete line */
int offset = 0;
if(p - b > 0)
    { /* get rid of \r */
     if(p[-1] == _T('\r'))
offset = 1;
    } /* get rid of \r */
//*line += CString(b, (p - b) - offset);
strncat(line,b,(p-b) - offset);
::PostMessage(target,UPM_LINE, (WPARAM)line, (LPARAM)pid);
b = p + 1;
//line = new CString;
line = (char *)calloc(1024,1);
} /* complete line */
    } /* convert and send */
} /* got data */

     DWORD err = ::GetLastError();

    ::CloseHandle(hreadFromChild);


     if(strlen(line) > 0)
::PostMessage(target,UPM_LINE, (WPARAM)line, (LPARAM)pid);
     else
delete line;
     
     DWORD status = 0;
     if(err != ERROR_BROKEN_PIPE)
status = err;
     
     ::SendMessage(target,UPM_FINISHED, status, (LPARAM)pid);
     delete this;
    } // Process::listener


The Process class was found on the net long ago and adapted for use in the IDE's. 

Paul.
Ionic Wind Support Team

Todd Riggins

So it's like polling for input from the other program via a process. Ok, time to study this! Msdn time...

You asked me what language I was using this for... Aurora. I just need to figure how to convert and use it.

Thanks for the info Paul. I didnt realize what I was stepping into there, but it should be doable for me.

Brought to you buy: http://www.exodev.com

Todd Riggins

Slowly but surely...

Learned that I had to use the ShellApi.

Using the mdidemo.src, I threw this in the case for "open":


String SEIVerb,SEIFile,SEIParams,SEIDir;
SEIVerb="open";
SEIFile="acparse.exe";
SEIParams="C:\Programming\Aurora\projects\Pipe\pipe.src C:\Programming\Aurora\projects\Pipe\mypipeout.asm";
SEIDir="C:\Programming\Aurora\bin";

SHELLEXECUTEINFO ShellExecInfo;
ShellExecInfo.cbSize=sizeof(SHELLEXECUTEINFO);
ShellExecInfo.fMask=0;
ShellExecInfo.hwnd=m_hWnd;
ShellExecInfo.lpVerb=SEIVerb;
ShellExecInfo.lpFile=SEIFile;
ShellExecInfo.lpParameters=SEIParams;
ShellExecInfo.lpDirectory=SEIDir;

int retval;
retval=ShellExecuteEx(&ShellExecInfo);

m_execProcess=ShellExecInfo.hProcess;


Thats just an example presented here. Obviously one would have to hard code there own paths and filenames.
The m_hWnd is the window handle.
The m_execProcess is of type HANDLE coded inside class MyFrame : MDIFrameWnd.

Oh, Im working off of saparo's aurora header's and thus so far have included these:
#include "windows.inc"
#include "ShellAPI.inc"

Tis exciting to actaully see this part work!  8)

Now onto playing with the CreatePipe and WaitForSingleObject functions...
Brought to you buy: http://www.exodev.com

ExMember001

Hi Todd,
do you got the pipe working? ;)
well i would like to know how you done it...
because i'm trying to figure Paul c++ code above too  ;D

ExMember001


Todd Riggins

Quote from: KrYpT on September 16, 2007, 02:58:02 AM
there's a pipe class in the CCL
http://www.ionicwind.com/forums/index.php/topic,288.0.html
check this out!

i never think about it ;)


Hi Krypt, yeah I finally saw that after I started to figure this stuff out. I glanced over it and it's probably exactly what I need, but I decided to go for the gold and learn the guts of getting all of this to work. I still need to figure threading and then the actual pipe stuff.

Unfortunitely, I had stopped my progress on it. If I have time today, I'll see if I can conitnue on. Basically, IF you are waiting on me, it might take a while.  :-\
Brought to you buy: http://www.exodev.com

Todd Riggins

Also after figuring out how to create a process with the code given in the createprocess topic I made, I dont think threading is needed. I should be able to use pipes without threading. hrmmm...
Brought to you buy: http://www.exodev.com

ExMember001

i'm not waiting ;)
but its a tough one when you don't know what's your doing :)

would like to see the source of the ccl but seem like i need to buy it to look at it ...