CpmLogic.icl 17.8 KB
Newer Older
1 2 3 4 5
implementation module CpmLogic

/**
 * CPM imports
 */
6
import AbsSyn, CpmPaths
7 8 9 10 11 12 13 14 15

/**
 * CleanIDE imports
 */
import IdeState, logfile, PmDriver, PmEnvironment, PmProject, set_return_code, UtilIO, UtilStrictLists

/**
 * Clean Platform imports
 */
16
import Text
Bas Lijnse's avatar
Bas Lijnse committed
17
import Data.Func, Data.Error, Data.List
18
import System.Directory, System.File, System.FilePath
19 20 21 22

/**
 * Clean libraries imports
 */
23
import StdBool, StdEnum, StdMisc, StdTuple, StdArray
24 25 26 27

/**
 * Execute a general CPM action
 */
28
doCpmAction :: String String !CpmAction !*World -> *World
29 30 31 32 33
doCpmAction cleanhome  pwd  CpmMake           world = doMake cleanhome pwd world
doCpmAction cleanhome  pwd  (Project pn pa)   world = doProjectAction cleanhome pwd pn pa world
doCpmAction cleanhome  pwd  (Module mn ma)    world = doModuleAction cleanhome mn ma world
doCpmAction cleanhome  pwd  (Environment ea)  world = doEnvironmentAction cleanhome pwd ea world
doCpmAction _          _    _                 world =
34 35
  help  "cpm <target>"
    [  "Where <target> is one of the following:"
36 37 38 39 40 41 42
    ,  "  <projectname> [--force] [--envs=filename] : build project <projectname>."
    ,  "                                              Optionally force build (default: 'false')"
    ,  "                                              Optionally specify the environments file (default: 'IDEEnvs')"
    ,  "  project <projectfile>                     : project actions"
    ,  "  module <modulename>                       : module actions"
    //,  "  environment                               : environment actions"
    ,  "  make                                      : build all projects in the current directory"
43 44 45
    ,  ""
    ,  "Execute `cpm <target> help` to get help for specific actions."] world

46 47 48
/**
 * Find all project files in the current working directory and build them
 */
49
doMake :: String !String !*World -> *World
50 51 52 53 54 55 56 57
doMake cleanhome pwd world
  # (mbErr, world) = readDirectory pwd world
  = case mbErr of
      Error _     -> error "Failed to read current directory" world
      Ok entries  -> case filter (\entry -> endsWith ".prj" entry) entries of
                       []  -> error ("No project file found in " +++ pwd) world
                       xs  -> foldr (\pn -> doProjectAction cleanhome pwd pn (BuildProject False EnvsFileName)) world xs

58 59 60 61 62 63 64
/**
 * Default compiler options. Currently it is a simple alias for
 * forwards-compatibility.
 */
compilerOptions :: CompilerOptions
compilerOptions = DefaultCompilerOptions

65 66 67 68 69 70 71
getLine :: *World -> *(String, *World)
getLine world
  # (console, world)  = stdio world
  # (line, console)   = freadline console
  # (_, world)        = fclose console world
  = (line, world)

72 73 74
/**
 * Execute project-specific actions
 */
75
doProjectAction :: String String String ProjectAction *World -> *World
76 77
doProjectAction cleanhome pwd  pn  CreateProject world
  //Check if main module exists
78 79 80 81
  # (exists,world)  = fileExists mainmodule world
  | not exists //       = error ("Main module " +++ mainmodule +++ " does not exist.") world
    # world         = showLines ["Main module " +++ mainmodule +++ " does not exist. Create it? [y/n]"] world
    # (line, world) = getLine world
82 83
    | line.[0] == 'y' = mkMainAndProject world
    | otherwise       = error ("Failed to create project. Need " +++ mainmodule) world
84
  | otherwise       = mkProject world
85
  where
86 87 88
  basefilename = dropExtension pn
  mainmodule   = addExtension basefilename "icl"

89 90 91 92
  mkMainAndProject world
    # world = doModuleAction "" mainmodule (CreateModule ApplicationModule) world
    = mkProject world
  mkProject world
93 94 95 96 97
    # edit_options   = {eo={newlines=NewlineConventionUnix},pos_size=NoWindowPosAndSize}
    //Create project file using the Clean IDE libraries
    # prj            = PR_NewProject mainmodule edit_options compilerOptions DefCodeGenOptions
                         DefApplicationOptions [!!] DefaultLinkOptions
    # project        = PR_SetRoot mainmodule edit_options compilerOptions prj
98 99
    # projectfile    = addExtension basefilename "prj"
    = saveProject cleanhome pwd project projectfile world
100 101

doProjectAction cleanhome pwd  pn  ShowProject world
102 103 104 105
  # (proj_path, project, ok, world) = openProject pwd pn cleanhome world
  | not ok
	= world
  = showLines  [  "Content of " +++ proj_path +++ ":"
106 107 108 109 110 111 112 113
               ,  "ProjectRoot..: " +++ PR_GetRelativeRootDir project
               ,  "Target.......: " +++ PR_GetTarget project
               ,  "Executable...: " +++ PR_GetExecPath project
               ,  "Paths........:"
               :  showPaths project
               ] world

doProjectAction cleanhome pwd  pn  (BuildProject force ideenvs) world
114 115 116 117 118 119 120
  # (envs, world)                = readIDEEnvs cleanhome ideenvs world
  # (proj_path, proj, ok, world) = openProject pwd pn cleanhome world
  | not ok
	= world
  # (console, world)             = stdio world
  # iniGeneral                   = initGeneral True compilerOptions cleanhome proj_path proj envs console
  # {ls, gst_world}              = pinit force {ls=iniGeneral,gst_world=world,gst_continue_or_stop=False}
121
  = gst_world
122 123 124
  where
  pinit force_rebuild gst = BringProjectUptoDate force_rebuild cleanup gst
  cleanup exepath bool1 bool2 ps = abortLog False "" ps
125

126 127 128 129 130
doProjectAction cleanhome pwd pn  (ProjectPath pa) world
  # (proj_path, project, ok, world) = openProject pwd pn cleanhome world
  | not ok
	= world
  = doProjectPathAction cleanhome pwd pn project pa world
131

132 133
doProjectAction cleanhome pwd pn (SetRelativeRoot target) world
  = withProject pwd pn cleanhome (PR_SetRelativeRootDir target) world
134

135 136
doProjectAction cleanhome pwd pn (SetTarget target) world
  = withProject pwd pn cleanhome (PR_SetTarget target) world
137

138 139
doProjectAction cleanhome pwd pn (SetExec exec) world
  = withProject pwd pn cleanhome (PR_SetExecPath exec) world
140

141
doProjectAction cleanhome pwd pn (SetProjectOptions project_options) world
142
	= withProject pwd pn cleanhome (set_project_options project_options) world
143 144 145 146 147 148 149 150 151 152 153
where
	set_project_options [project_option:project_options] project
		# project = set_project_option project_option project
		= set_project_options project_options project
	set_project_options [] project
		= project

	set_project_option DynamicsOn project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & dynamics = True} project
	set_project_option DynamicsOff project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & dynamics = False} project
154 155 156 157
	set_project_option GenericFusionOn project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & generic_fusion = True} project
	set_project_option GenericFusionOff project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & generic_fusion = False} project
158 159 160 161
	set_project_option DescExLOn project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & desc_exl = True} project
	set_project_option DescExLOff project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & desc_exl = False} project
162 163 164
	set_project_option (HeapSize hs) project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & hs = hs} project
	set_project_option (StackSize ss) project
165
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & ss = ss} project
166 167
	set_project_option (Output output) project
		= PR_SetApplicationOptions {PR_GetApplicationOptions project & o = output} project
168 169 170 171
	set_project_option LinkerGenerateSymbolsOn project
		= PR_SetLinkOptions project {PR_GetLinkOptions project & generate_symbol_table=True}
	set_project_option LinkerGenerateSymbolsOff project
		= PR_SetLinkOptions project {PR_GetLinkOptions project & generate_symbol_table=False}
172

173
doProjectAction _          _  _   _    world             =
174
  help "cpm project <projectfile> <action>"
175
    [  "Where <action> is one of the following"
176 177 178 179 180
    ,  "  create                            : create a new project"
    ,  "  show                              : show project information"
    ,  "  build [--force] [--envs=filename] : build the project. Optionally force build (default: 'false')"
    ,  "                                      Optionally specify the environments file (default: 'IDEEnvs')"
    ,  "  path                              : manage project paths"
John van Groningen's avatar
John van Groningen committed
181
    ,  "  root                              : set the project root"
182 183 184 185 186 187 188 189 190 191
    ,  "  target <env>                      : set target environment to <env>"
    ,  "  exec <execname>                   : set executable name to <execname>"
    ,  "  set option [option]               : Set one or more of the following options:"
    ,  "                                    : -generic_fusion,-ngeneric_fusion"
    ,  "                                    :     Enable or disable generic fusion"
    ,  "                                    : -dynamics,-ndynamics"
    ,  "                                    :     Enable or disable dynamics"
    ,  "                                    : -descexl,-descexl"
    ,  "                                    :     Enable or disable descriptor generation and label exporting"
    ,  "                                    :     This translates to passing -desc and -exl to cocl"
192 193 194
    ,  "                                    : -b,-nr,-nc,-sc"
    ,  "                                    :     Set the output option to BasicValuesOnly, NoReturnType,"
    ,  "                                    :     NoConsole or ShowConstructors respectively"
195 196
    ] world

197 198 199
/**
 * Execute environment-specific actions
 */
200
doEnvironmentAction :: String String EnvironmentAction *World -> *World
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
doEnvironmentAction cleanhome  pwd  ListEnvironments                world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (ImportEnvironment ef)          world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (RemoveEnvironment en)          world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (ShowEnvironment en)            world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (ExportEnvironment en)          world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (CreateEnvironment en)          world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (RenameEnvironment en en`)      world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (SetEnvironmentCompiler en cp)  world = error ("Not implemented") world
doEnvironmentAction cleanhome  pwd  (SetEnvironmentCodeGen en cp)   world = error ("Not implemented") world
doEnvironmentAction _          _    _                               world =
  help "cpm environment <action>"
    [  "Where <action> is one of the following"
    ,  "  list                                  : list all available environments"
    ,  "  import <filepath>                     : import an environement from file <filepath>"
    ,  "  create <envname>                      : create a new environment with name <envname>"
    ,  "  remove <envname>                      : remove evironment <envname>"
    ,  "  show <envname>                        : show environment <envname>"
    ,  "  export <envname>                      : export environment <envname>"
    ,  "  rename <envname> <envname`>           : rename environment <envname> to <envname`>"
    ,  "  setcompiler <envname> <compilername>  : set compiler for <envname> to <compilername>"
    ,  "  setcodegen <envname> <codegenname>    : set codegen for <envname> to <codegenname>"
    ] world

224 225 226
/**
 * Modify a project
 */
227 228 229 230 231 232
withProject :: !String !String !String (Project -> Project) *World -> *World
withProject pwd pn cleanhome f world
  # (project_path, project, ok, world) = openProject pwd pn cleanhome world
  | not ok
	= world
  = saveProject cleanhome pwd (f project) project_path world
233 234 235 236

/**
 * Execute path-related project actions
 */
237 238 239
doProjectPathAction :: String String String Project PathAction *World -> *World
doProjectPathAction cleanhome pwd pn project (AddPathAction path) world
  = doModPaths cleanhome pwd pn project ((:!) (GetLongPathName path)) world
240

241 242
doProjectPathAction cleanhome pwd pn project (RemovePathAction i) world
  = doModPaths cleanhome pwd pn project (rmStrictListIdx i) world
243

244 245
doProjectPathAction _ _ _ project ListPathsAction world
  = showLines ["Paths for project:" : showPaths project] world
246

247 248
doProjectPathAction cleanhome pwd pn project (MovePathAction i pdir) world
  = doModPaths cleanhome pwd pn project (moveStrictListIdx i pdir) world
249

250 251
doProjectPathAction _          _ _   _     _  world
  = help "cpm project <projectname.prj> path <action>"
252 253 254 255 256 257 258 259 260
    [  "Where <action> is one of the following"
    ,  "  add <path>          : add a path to the project"
    ,  "  list                : list all project paths and their index"
    ,  "  remove <i>          : remove path <i> from the list of projects"
    ,  "  move <i> <up|down>  : move path <i> up or down one position" ] world

/**
 * Collect all project paths in a list with an index prefixed
 */
261
showPaths :: !Project -> [String]
262 263 264 265 266 267 268 269
showPaths project = map f (zip2 [0..] (StrictListToList (PR_GetPaths project)))
  where f (n, p)  = "  [" +++ toString n +++ "]  " +++ p

/**
 * Modify the list of paths in a project given a modification function which
 * takes a strict list of project paths and returns a strict list of project
 * paths.
 */
270 271
doModPaths :: !String !String !String !Project ([!String!] -> [!String!]) *World -> *World
doModPaths cleanhome pwd pn project f world
272 273
  # paths = PR_GetPaths project
  # prj   = PR_SetPaths False paths (f paths) project
274
  # world = saveProject cleanhome pwd prj pn world
275 276 277 278 279
  = showLines ["Successfully modified project paths"] world

/**
 * Open a project file
 */
280 281 282 283 284 285 286
openProject :: !FilePath !FilePath !FilePath !*World -> (!FilePath, !Project, Bool, !*World)
openProject pwd pn cleanhome world
  # proj_path                = GetLongPathName (pwd </> pn)
  # ((prj, ok, err), world)  = accFiles (ReadProjectFile proj_path cleanhome) world
  | not ok || err <> ""
	= (proj_path, prj, ok, error err world)
  = (proj_path, prj, ok, world)
287 288 289 290

/**
 * Save a project back to its project file
 */
291 292 293 294 295 296 297
saveProject :: !FilePath !FilePath !Project !FilePath !*World -> *World
saveProject cleanhome pwd prj projectfile world
  # proj_path = GetLongPathName projectfile
  # (ok, world) = accFiles (SaveProjectFile proj_path prj cleanhome) world
  | not ok
	= error "Error saving project" world
  = world
298 299 300 301 302

/**
 * Remove an item from a strict list at a given index. Abort execution if the
 * index is out of bounds.
 */
303
rmStrictListIdx :: !Int [!a!] -> [!a!]
304 305 306 307 308 309 310 311
rmStrictListIdx 0  (_ :! t)          = t
rmStrictListIdx n  (h :! t) | n > 0  = h :! (rmStrictListIdx (n - 1) t)
rmStrictListIdx n  _                 = abort ("Index " +++ toString n +++ " out of bounds")

/**
 * Move a path at a given index up or down the list of paths. Abort execution
 * if the index is out of bounds.
 */
312
moveStrictListIdx :: !Int PathDirection [!a!] -> [!a!]
313 314 315
moveStrictListIdx i dir xs
  | i < 0 || i > (LLength xs - 1)  = abort ("Index " +++ toString i +++ " out of bounds")
  | otherwise                      = ListToStrictList (msl dir (splitAt i (StrictListToList xs)))
316 317 318 319 320 321 322 323 324 325
  where  msl MovePathUp      ([], xs)        = xs
         msl MovePathUp      (xs, [x:ys])    = (init xs) ++ [x : (last xs) : ys]
         msl MovePathDown    ([], [x:y:ys])  = [y:x:ys]
         msl MovePathDown    (xs, [])        = xs
         msl MovePathDown    (xs, [y])       = xs ++ [y]
         msl MovePathDown    (xs, [x:y:ys])  = xs ++ [y:x:ys]
         msl MovePathTop     (xs, [])        = xs
         msl MovePathTop     (xs, [y:ys])    = [y:xs] ++ ys
         msl MovePathBottom  (xs, [])        = xs
         msl MovePathBottom  (xs, [y:ys])    = xs ++ ys ++ [y]
326 327 328 329

/**
 * Execute module-related actions
 */
330
doModuleAction :: String !String !ModuleAction !*World -> *World
331
doModuleAction _ mn  (CreateModule mt) world
332
  # (dclexists, world)  = fileExists dclnm world
333
  | dclexists           = error ("Definition module '" +++ dclnm +++ "' already exists.") world
334
  # (iclexists, world)  = fileExists iclnm world
335
  | iclexists           = error ("Implementation module '" +++ iclnm +++ "' already exists.") world
336
  = writeMods mt world
337 338 339 340
  where
  basenm     = dropExtension mn
  dclnm      = addExtension basenm "dcl"
  iclnm      = addExtension basenm "icl"
341

342
  mkmod mty  = mty +++ "module " +++ basenm
343

344 345 346 347
  writeMods ApplicationModule world = writeicl ApplicationModule world
  writeMods LibraryModule world
    # world = writeicl ApplicationModule world
    = writedcl world
348

349 350
  writeicl ApplicationModule  world = writeicl` "" world
  writeicl LibraryModule      world = writeicl` "implementation " world
351

352
  writeicl` pref world = writemod iclnm pref ("Failed to write implementation module '" +++ basenm +++ "'") world
353

354
  writedcl world = writemod dclnm "definition " ("Failed to write definition module '" +++ basenm +++ "'") world
355

356 357 358 359
  writemod nm pref errmsg world
    # (me, world)  = writeFile nm (mkmod pref) world
    | isError me   = error errmsg world
    = world
360 361 362 363

doModuleAction _ _   _  world                =
  help "cpm module <modulename> <action>"
    [  "Where <action> is one of the following"
364 365 366 367
    ,  "  create [application|library]  : create a new module. Optionally specify module type (default: 'library')"
    //,  "  check <projectname.prj>       : type-check module in the context of project <projectname.prj>"
    //,  "  compile <projectname.prj>     : compile module in the context of project <projectname.prj>"
    ] world
368 369 370 371

/**
 * Show an error message
 */
372
error :: !String !*World -> *World
373
error message world
374 375
  # stderr     = fwrites message stderr
  # (ok,world) = fclose stderr world
376 377 378 379 380 381
  = set_return_code_world (-1) world

/**
 * Show a help message
 */
help :: !String ![String] !*World -> *World
382 383 384 385 386 387
help cmd lines world
  # lines` = [ "CPM: Clean Project Manager"
             : ""
             : "Usage: " +++ cmd
             : lines]
  = showLines lines` world
388 389 390 391 392 393 394

/**
 * Given a list of strings, concatenate them to a single string with newlines
 * in between, then print that new string to console.
 */
showLines :: ![String] !*World -> *World
showLines lines world
395 396
  # (console, world) = stdio world
  # console          = seqSt (\s -> fwrites (s +++ "\n")) lines console
397
  = snd $ fclose console world