VDBStartup.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Fri, 25 Jan 2019 13:33:51 +0000
changeset 143 df7f89efd39d
parent 131 815f0bf03155
child 180 a47acd6d73ca
permissions -rw-r--r--
A complete rewrite of simple console ..that is not using `TerminalView`. The original (previous) implementation had various problems that were hard to fix, namely loosing some stream output in some cases. New (current) implementation uses custom console view (1VDBSimpleDebuggerConsoleView`) based on `TextCollector` rather than `TerminalView`. The resulting code is much much simpler, it does not use internal pipes nor REPL / pipe reader processes. Whole REPL runs completely in UI process.

"
jv:vdb - Visual / VM Debugger
Copyright (C) 2015-now Jan Vrany

This software is licensed under 'Creative Commons Attribution-NonCommercial 4.0 International License'

You may find a full license text in LICENSE.txt or at http://creativecommons.org/licenses/by-nc/4.0/
"
"{ Package: 'jv:vdb' }"

"{ NameSpace: Smalltalk }"

StandaloneStartup subclass:#VDBStartup
	instanceVariableNames:''
	classVariableNames:'Standalone'
	poolDictionaries:''
	category:'VDB-UI'
!

!VDBStartup class methodsFor:'documentation'!

copyright
"
jv:vdb - Visual / VM Debugger
Copyright (C) 2015-now Jan Vrany

This software is licensed under 'Creative Commons Attribution-NonCommercial 4.0 International License'

You may find a full license text in LICENSE.txt or at http://creativecommons.org/licenses/by-nc/4.0/
"
! !

!VDBStartup class methodsFor:'constants & defaults'!

applicationRegistryPath
    "the key under which this application stores its process ID in the registry
     as a collection of path-components.
     i.e. if #('foo' 'bar' 'baz') is returned here, the current applications ID will be stored
     in HKEY_CURRENT_USER\Software\foo\bar\baz\CurrentID.
     (would also be used as a relative path for a temporary lock file under unix).
     Used to detect if another instance of this application is already running."

    ^ #('vdb')

    "Modified: / 21-09-2014 / 01:29:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

applicationUUID
    "answer an application-specific unique uuid.
     This is used as the name of some exclusive OS-resource, which is used to find out,
     if another instance of this application is already running.
     Under win32, a mutex is used; under unix, an exclusive file in the tempDir could be used.
     If redefined, please return a real UUID (i.e. UUID fromString:'.....') and not a string or
     similar possibly conflicting identifier.
     You can paste a fresh worldwide unique id via the editor's more-misc-paste UUID menuFunction."

    ^ UUID fromString:'57b09330-4126-11e4-a80f-606720e43e2c'

    "Modified: / 21-09-2014 / 01:29:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!VDBStartup class methodsFor:'defaults'!

allowDebugOption
    "enable/disable the --debug startup option.
     Can be redefined in subclasses to enable it"

    ^ true

    "Created: / 08-09-2014 / 19:30:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!VDBStartup class methodsFor:'private'!

loadPreferenceFile: file

    "Created: / 07-06-2017 / 09:49:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!VDBStartup class methodsFor:'queries'!

applicationName
    ^ 'vdb'

    "Created: / 06-06-2017 / 22:50:44 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!VDBStartup class methodsFor:'startup'!

main:argv
    "Application entry point. `argv` is the array of command arguments (as Array of Strings)"

    | optparser positional settingsFile settingsSuppressed cmd noPTY replay
      programExecutable programArgs programPid attach
      debugger debuggerApp |

    "/ This is awkward. We HAVE TO load prefetences when VDB is compiled standalone
    "/ application or run from a command line (`--load jv:vdb` --run VDBStarup`.
    "/ We MUST NOT load preferences when `VDBStarup main` is run from running IDE.
    "/ for example while developing.
    "/
    "/ The trick is that there's no good and easy way to check this, so we have to
    "/ check whether this method is called from `Smalltalk >> start`. Bleh.
    Standalone := Smalltalk isStandAloneApp
                    or:[ thisContext sender method == (Smalltalk class compiledMethodAt: #start) ].
    settingsSuppressed := Standalone not.

    noPTY := false.
    replay := false.
    attach := false.

    "/ Parse options...
    optparser := CmdLineParser new
                    ignoreUnknownOptions: true;
                    on: #('--help') do:[
                        self usage
                    ];
                    on: #('--preferences') do:[:filename |
                        | file |

                        file := filename asFilename.
                        file isReadable ifFalse:[
                            self error: 'preference file does not exists or not readable: ' , filename.
                        ].
                        file isRegularFile ifFalse:[
                            self error: 'preference file is not a regular file: ' , filename.
                        ].
                        settingsFile := file.
                    ];
                    on: #('--no-preferences') do:[
                        settingsSuppressed := true
                    ];
                    on: #('--replay') do:[
                        replay := true
                    ];
                    on: #('-d' '--gdb') do:[ :str |
                        cmd := str
                    ];
                    on: #('--no-pty') do:[
                        noPTY := true
                    ];
                    yourself.
    [
        positional := optparser parse:argv.
    ] on: CmdLineOptionError do:[:ex |
        self error: ex description.
    ].


    "/ Now validate and process options
    settingsSuppressed ifFalse:[
        | settings |

        settingsFile notNil ifTrue:[
            settingsFile exists ifFalse:[
                self error: 'preference file does not exist: ', settingsFile pathName
            ].
            settingsFile isDirectory ifTrue:[
                self error: 'preference file is not a regular file: ', settingsFile pathName
            ].
            settingsFile isReadable ifFalse:[
                self error: 'preference file is not a readable (check permissions): ', settingsFile pathName
            ].
            settings := UserPreferences loadSettingsFrom: settingsFile.
        ] ifFalse:[
            settings := UserPreferences loadSettings.
        ].
        UserPreferences setCurrent: settings.
    ].

    replay ifTrue:[ 
        OperatingSystem isLinuxLike ifFalse:[ 
            self error: 'replay not supported on this platform'.
        ].
        RR available ifFalse:[ 
            self error: 'cannot replay because rr not available'
        ].
    ].

    "/ Parse positional arguments - there are two forms:
    "/
    "/   vdb [OPTIONS] [PROGRAM [ARGS]]
    "/   vdb [OPTIONS] [PID]
    "/
    "/ [OPTIONS] have already been processed, the rest is in `positional`
    "/ variable

    positional notEmpty ifTrue:[
        programExecutable := positional first.
        programExecutable asFilename exists ifFalse:[
            "Try to find the executable in PATH..."

            | path |

            path := OperatingSystem pathOfCommand: programExecutable.
            path notNil ifTrue:[
                programExecutable := path.
            ].
        ].
        programPid := Integer fromString: positional first onError: [ nil ].
        programArgs := positional copyFrom: 2.

        replay ifTrue:[ 
            programArgs notEmptyOrNil ifTrue:[ 
                self error: 'cannot specify program args when replaying'.
            ].
        ] ifFalse:[
            "/ If * programExecutable does not exists
            "/    * AND programPid is not nil (i.e., first positional argument can be converted to an integer)
            "/    * AND programArguments are empty
            "/ then interpret positional argument as PID and attach to it.
            "/ Otherwise, interpret positional arguments
            (programExecutable asFilename exists not and: [ programPid notNil and: [ programArgs isEmpty ]]) ifTrue:[
                attach := true.
            ] ifFalse:[
                programExecutable asFilename exists ifFalse:[
                    self error: 'cannot find program executable: ', programExecutable.
                ].
            ].
        ].
    ].

    Debugger := DebugView ? MiniDebugger.
    Inspector := InspectorView ? MiniInspector.

    noPTY ifTrue:[ 
        debugger := GDBDebugger newWithProcess: (GDBStXSimpleProcess newWithCommand: cmd).
    ] ifFalse:[ 
        debugger := GDBDebugger newWithProcess: (GDBLocalProcess newWithCommand: cmd).
    ].


    attach ifTrue:[
        debugger attach: programPid
    ] ifFalse:[
        programExecutable notNil ifTrue:[
            debugger executable: programExecutable arguments: programArgs.
        ].
    ].
    Smalltalk openDisplay.
    debuggerApp := VDBDebuggerApplication new.
    debuggerApp debugger: debugger.
    debuggerApp open.
    replay ifTrue:[ 
        debuggerApp doAttachToRR
    ]. 

    "
        VDBStartup main: #()
        VDBStartup main: #('ls')
        VDBStartup main: #('/bin/ls' '/tmp')
    "

    "Modified: / 13-12-2018 / 15:32:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

usage
    Transcript nextPutAll:'usage: '; nextPutAll: self applicationName; nextPutAll: ' [OPTIONS] [PROGRAM [ARGS]] '; cr.
    Transcript nextPutAll:'       '; nextPutAll: self applicationName; nextPutAll: ' [OPTIONS] [PID]'; cr.
                                                                          "|"
    Transcript nextPutLine:'
options:
 --replay ..................... replay last rr record
 --no-pty ..................... do not use PTY-based GDB console even if current
                                platform supports it. 
 -d | --gdb CMD ............... use CMD to launch GDB (instead of platform
                                default).    
 --preference FILE ............ read user settings from FILE
 --no-preferences ............. do not read user settings at all
 --help ....................... output this message
'.
    Standalone ifTrue:[
        Smalltalk exit: 0.
    ].

    "Modified: / 13-12-2018 / 15:29:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!VDBStartup class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
! !