@@ -1,4 +1,10 @@ |
| 1 | 1 | module fgof_devloop |
| 2 | + use fgof_process, only : & |
| 3 | + FGOF_PROCESS_OK, & |
| 4 | + process_command, & |
| 5 | + process_options, & |
| 6 | + process_result, & |
| 7 | + run_process => run |
| 2 | 8 | use fgof_watch_types, only : & |
| 3 | 9 | FGOF_WATCH_EVT_CREATED, & |
| 4 | 10 | FGOF_WATCH_EVT_MODIFIED, & |
@@ -12,14 +18,21 @@ module fgof_devloop |
| 12 | 18 | FGOF_DEVLOOP_DECISION_RESTART, & |
| 13 | 19 | FGOF_DEVLOOP_DECISION_RUN, & |
| 14 | 20 | FGOF_DEVLOOP_DECISION_STOP, & |
| 21 | + FGOF_DEVLOOP_COMMAND_BUILD, & |
| 22 | + FGOF_DEVLOOP_COMMAND_NONE, & |
| 23 | + FGOF_DEVLOOP_COMMAND_RUN, & |
| 24 | + FGOF_DEVLOOP_COMMAND_SMOKE, & |
| 15 | 25 | FGOF_DEVLOOP_TRIGGER_CHANGE, & |
| 16 | 26 | FGOF_DEVLOOP_TRIGGER_MANUAL, & |
| 17 | 27 | FGOF_DEVLOOP_TRIGGER_NONE, & |
| 18 | 28 | FGOF_DEVLOOP_TRIGGER_START, & |
| 29 | + devloop_command_result, & |
| 30 | + devloop_command_spec, & |
| 19 | 31 | devloop_cycle, & |
| 20 | 32 | devloop_decision, & |
| 21 | 33 | devloop_options, & |
| 22 | 34 | devloop_state, & |
| 35 | + devloop_supervision_result, & |
| 23 | 36 | devloop_trigger, & |
| 24 | 37 | devloop_watch_summary |
| 25 | 38 | implicit none |
@@ -28,20 +41,29 @@ module fgof_devloop |
| 28 | 41 | character(len=*), parameter :: FGOF_DEVLOOP_BACKEND_MODEL = "model" |
| 29 | 42 | |
| 30 | 43 | public :: begin_devloop_cycle |
| 44 | + public :: clear_devloop_command_result |
| 45 | + public :: clear_devloop_command_spec |
| 31 | 46 | public :: clear_devloop_cycle |
| 32 | 47 | public :: clear_devloop_decision |
| 33 | 48 | public :: clear_devloop_options |
| 34 | 49 | public :: clear_devloop_state |
| 50 | + public :: clear_devloop_supervision_result |
| 35 | 51 | public :: clear_devloop_trigger |
| 36 | 52 | public :: clear_devloop_watch_summary |
| 37 | 53 | public :: devloop_backend_name |
| 54 | + public :: devloop_build_command |
| 38 | 55 | public :: devloop_change_trigger |
| 56 | + public :: devloop_command_result |
| 57 | + public :: devloop_command_spec |
| 39 | 58 | public :: devloop_cycle |
| 40 | 59 | public :: devloop_decision |
| 41 | 60 | public :: devloop_manual_trigger |
| 42 | 61 | public :: devloop_options |
| 62 | + public :: devloop_run_command |
| 63 | + public :: devloop_smoke_command |
| 43 | 64 | public :: devloop_start_trigger |
| 44 | 65 | public :: devloop_state |
| 66 | + public :: devloop_supervision_result |
| 45 | 67 | public :: devloop_summarize_watch_events |
| 46 | 68 | public :: devloop_trigger |
| 47 | 69 | public :: devloop_watch_failure_summary |
@@ -53,10 +75,16 @@ module fgof_devloop |
| 53 | 75 | public :: FGOF_DEVLOOP_DECISION_RESTART |
| 54 | 76 | public :: FGOF_DEVLOOP_DECISION_RUN |
| 55 | 77 | public :: FGOF_DEVLOOP_DECISION_STOP |
| 78 | + public :: FGOF_DEVLOOP_COMMAND_BUILD |
| 79 | + public :: FGOF_DEVLOOP_COMMAND_NONE |
| 80 | + public :: FGOF_DEVLOOP_COMMAND_RUN |
| 81 | + public :: FGOF_DEVLOOP_COMMAND_SMOKE |
| 56 | 82 | public :: FGOF_DEVLOOP_TRIGGER_CHANGE |
| 57 | 83 | public :: FGOF_DEVLOOP_TRIGGER_MANUAL |
| 58 | 84 | public :: FGOF_DEVLOOP_TRIGGER_NONE |
| 59 | 85 | public :: FGOF_DEVLOOP_TRIGGER_START |
| 86 | + public :: run_devloop_command |
| 87 | + public :: run_devloop_cycle |
| 60 | 88 | public :: should_start_on_open |
| 61 | 89 | public :: start_devloop |
| 62 | 90 | public :: stop_devloop |
@@ -126,6 +154,50 @@ contains |
| 126 | 154 | summary%watch_error_message = "" |
| 127 | 155 | end function clear_devloop_watch_summary |
| 128 | 156 | |
| 157 | + function clear_devloop_command_spec() result(spec) |
| 158 | + type(devloop_command_spec) :: spec |
| 159 | + |
| 160 | + spec%kind = FGOF_DEVLOOP_COMMAND_NONE |
| 161 | + spec%enabled = .false. |
| 162 | + spec%options = process_options() |
| 163 | + spec%label = "" |
| 164 | + end function clear_devloop_command_spec |
| 165 | + |
| 166 | + function clear_devloop_command_result() result(command_result) |
| 167 | + type(devloop_command_result) :: command_result |
| 168 | + |
| 169 | + command_result%kind = FGOF_DEVLOOP_COMMAND_NONE |
| 170 | + command_result%requested = .false. |
| 171 | + command_result%skipped = .true. |
| 172 | + command_result%launched = .false. |
| 173 | + command_result%completed = .false. |
| 174 | + command_result%succeeded = .false. |
| 175 | + command_result%timed_out = .false. |
| 176 | + command_result%exit_code = 0 |
| 177 | + command_result%process_error_code = FGOF_PROCESS_OK |
| 178 | + command_result%process = clear_process_result() |
| 179 | + command_result%label = "" |
| 180 | + command_result%error_message = "" |
| 181 | + end function clear_devloop_command_result |
| 182 | + |
| 183 | + function clear_devloop_supervision_result() result(supervision) |
| 184 | + type(devloop_supervision_result) :: supervision |
| 185 | + |
| 186 | + supervision%cycle = clear_devloop_cycle() |
| 187 | + supervision%decision = clear_devloop_decision() |
| 188 | + supervision%build = clear_devloop_command_result() |
| 189 | + supervision%run = clear_devloop_command_result() |
| 190 | + supervision%smoke = clear_devloop_command_result() |
| 191 | + supervision%command_count = 0 |
| 192 | + supervision%failed_command_kind = FGOF_DEVLOOP_COMMAND_NONE |
| 193 | + supervision%last_exit_code = 0 |
| 194 | + supervision%process_error_code = FGOF_PROCESS_OK |
| 195 | + supervision%started = .false. |
| 196 | + supervision%succeeded = .false. |
| 197 | + supervision%failed = .false. |
| 198 | + supervision%timed_out = .false. |
| 199 | + end function clear_devloop_supervision_result |
| 200 | + |
| 129 | 201 | function clear_devloop_state() result(state) |
| 130 | 202 | type(devloop_state) :: state |
| 131 | 203 | |
@@ -202,6 +274,33 @@ contains |
| 202 | 274 | end if |
| 203 | 275 | end function devloop_manual_trigger |
| 204 | 276 | |
| 277 | + function devloop_build_command(command_value, options, label) result(spec) |
| 278 | + type(process_command), intent(in) :: command_value |
| 279 | + type(process_options), intent(in), optional :: options |
| 280 | + character(len=*), intent(in), optional :: label |
| 281 | + type(devloop_command_spec) :: spec |
| 282 | + |
| 283 | + spec = make_devloop_command(FGOF_DEVLOOP_COMMAND_BUILD, command_value, options, label) |
| 284 | + end function devloop_build_command |
| 285 | + |
| 286 | + function devloop_run_command(command_value, options, label) result(spec) |
| 287 | + type(process_command), intent(in) :: command_value |
| 288 | + type(process_options), intent(in), optional :: options |
| 289 | + character(len=*), intent(in), optional :: label |
| 290 | + type(devloop_command_spec) :: spec |
| 291 | + |
| 292 | + spec = make_devloop_command(FGOF_DEVLOOP_COMMAND_RUN, command_value, options, label) |
| 293 | + end function devloop_run_command |
| 294 | + |
| 295 | + function devloop_smoke_command(command_value, options, label) result(spec) |
| 296 | + type(process_command), intent(in) :: command_value |
| 297 | + type(process_options), intent(in), optional :: options |
| 298 | + character(len=*), intent(in), optional :: label |
| 299 | + type(devloop_command_spec) :: spec |
| 300 | + |
| 301 | + spec = make_devloop_command(FGOF_DEVLOOP_COMMAND_SMOKE, command_value, options, label) |
| 302 | + end function devloop_smoke_command |
| 303 | + |
| 205 | 304 | function devloop_summarize_watch_events(events) result(summary) |
| 206 | 305 | type(watch_event), intent(in) :: events(:) |
| 207 | 306 | type(devloop_watch_summary) :: summary |
@@ -292,6 +391,71 @@ contains |
| 292 | 391 | end if |
| 293 | 392 | end function devloop_watch_trigger |
| 294 | 393 | |
| 394 | + function run_devloop_command(spec) result(command_result) |
| 395 | + type(devloop_command_spec), intent(in) :: spec |
| 396 | + type(devloop_command_result) :: command_result |
| 397 | + type(process_result) :: process_run_result |
| 398 | + |
| 399 | + command_result = clear_devloop_command_result() |
| 400 | + command_result%kind = spec%kind |
| 401 | + command_result%label = command_kind_label(spec%kind) |
| 402 | + if (allocated(spec%label)) then |
| 403 | + if (len(spec%label) > 0) command_result%label = spec%label |
| 404 | + end if |
| 405 | + |
| 406 | + if (.not. spec%enabled) return |
| 407 | + |
| 408 | + command_result%requested = .true. |
| 409 | + command_result%skipped = .false. |
| 410 | + process_run_result = run_process(spec%command, spec%options) |
| 411 | + command_result%process = process_run_result |
| 412 | + command_result%launched = process_run_result%launched |
| 413 | + command_result%completed = process_run_result%completed |
| 414 | + command_result%timed_out = process_run_result%timed_out |
| 415 | + command_result%exit_code = process_run_result%exit_code |
| 416 | + command_result%process_error_code = process_run_result%error_code |
| 417 | + command_result%error_message = process_run_result%error_message |
| 418 | + command_result%succeeded = process_run_result%error_code == FGOF_PROCESS_OK .and. & |
| 419 | + process_run_result%completed .and. & |
| 420 | + process_run_result%exited_normally .and. & |
| 421 | + process_run_result%exit_code == 0 |
| 422 | + end function run_devloop_command |
| 423 | + |
| 424 | + function run_devloop_cycle(state, trigger, build_command, run_command, smoke_command) result(supervision) |
| 425 | + type(devloop_state), intent(inout) :: state |
| 426 | + type(devloop_trigger), intent(in) :: trigger |
| 427 | + type(devloop_command_spec), intent(in), optional :: build_command |
| 428 | + type(devloop_command_spec), intent(in), optional :: run_command |
| 429 | + type(devloop_command_spec), intent(in), optional :: smoke_command |
| 430 | + type(devloop_supervision_result) :: supervision |
| 431 | + logical :: should_continue |
| 432 | + |
| 433 | + supervision = clear_devloop_supervision_result() |
| 434 | + supervision%cycle = begin_devloop_cycle(state, trigger) |
| 435 | + supervision%started = supervision%cycle%started |
| 436 | + if (.not. supervision%started) return |
| 437 | + |
| 438 | + should_continue = .true. |
| 439 | + if (present(build_command)) then |
| 440 | + supervision%build = run_devloop_command(build_command) |
| 441 | + call record_supervised_command(supervision, supervision%build, should_continue) |
| 442 | + end if |
| 443 | + |
| 444 | + if (should_continue .and. present(run_command)) then |
| 445 | + supervision%run = run_devloop_command(run_command) |
| 446 | + call record_supervised_command(supervision, supervision%run, should_continue) |
| 447 | + end if |
| 448 | + |
| 449 | + if (should_continue .and. present(smoke_command)) then |
| 450 | + supervision%smoke = run_devloop_command(smoke_command) |
| 451 | + call record_supervised_command(supervision, supervision%smoke, should_continue) |
| 452 | + end if |
| 453 | + |
| 454 | + supervision%succeeded = should_continue |
| 455 | + supervision%failed = .not. should_continue |
| 456 | + supervision%decision = finish_devloop_cycle(state, supervision%succeeded, supervision%last_exit_code) |
| 457 | + end function run_devloop_cycle |
| 458 | + |
| 295 | 459 | function begin_devloop_cycle(state, trigger) result(cycle) |
| 296 | 460 | type(devloop_state), intent(inout) :: state |
| 297 | 461 | type(devloop_trigger), intent(in) :: trigger |
@@ -362,6 +526,76 @@ contains |
| 362 | 526 | name = FGOF_DEVLOOP_BACKEND_MODEL |
| 363 | 527 | end function devloop_backend_name |
| 364 | 528 | |
| 529 | + function make_devloop_command(kind, command_value, options, label) result(spec) |
| 530 | + integer, intent(in) :: kind |
| 531 | + type(process_command), intent(in) :: command_value |
| 532 | + type(process_options), intent(in), optional :: options |
| 533 | + character(len=*), intent(in), optional :: label |
| 534 | + type(devloop_command_spec) :: spec |
| 535 | + |
| 536 | + spec = clear_devloop_command_spec() |
| 537 | + spec%kind = kind |
| 538 | + spec%enabled = .true. |
| 539 | + spec%command = command_value |
| 540 | + spec%options = process_options() |
| 541 | + if (present(options)) spec%options = options |
| 542 | + if (present(label)) then |
| 543 | + spec%label = label |
| 544 | + else |
| 545 | + spec%label = command_kind_label(kind) |
| 546 | + end if |
| 547 | + end function make_devloop_command |
| 548 | + |
| 549 | + function clear_process_result() result(process_run_result) |
| 550 | + type(process_result) :: process_run_result |
| 551 | + |
| 552 | + process_run_result%launched = .false. |
| 553 | + process_run_result%completed = .false. |
| 554 | + process_run_result%timed_out = .false. |
| 555 | + process_run_result%exited_normally = .false. |
| 556 | + process_run_result%exit_code = -1 |
| 557 | + process_run_result%term_signal = 0 |
| 558 | + process_run_result%stdout = "" |
| 559 | + process_run_result%stderr = "" |
| 560 | + process_run_result%error_code = FGOF_PROCESS_OK |
| 561 | + process_run_result%error_message = "" |
| 562 | + process_run_result%elapsed_ms = 0 |
| 563 | + end function clear_process_result |
| 564 | + |
| 565 | + function command_kind_label(kind) result(label) |
| 566 | + integer, intent(in) :: kind |
| 567 | + character(len=:), allocatable :: label |
| 568 | + |
| 569 | + select case (kind) |
| 570 | + case (FGOF_DEVLOOP_COMMAND_BUILD) |
| 571 | + label = "build" |
| 572 | + case (FGOF_DEVLOOP_COMMAND_RUN) |
| 573 | + label = "run" |
| 574 | + case (FGOF_DEVLOOP_COMMAND_SMOKE) |
| 575 | + label = "smoke" |
| 576 | + case default |
| 577 | + label = "none" |
| 578 | + end select |
| 579 | + end function command_kind_label |
| 580 | + |
| 581 | + subroutine record_supervised_command(supervision, command_result, should_continue) |
| 582 | + type(devloop_supervision_result), intent(inout) :: supervision |
| 583 | + type(devloop_command_result), intent(in) :: command_result |
| 584 | + logical, intent(inout) :: should_continue |
| 585 | + |
| 586 | + if (.not. command_result%requested) return |
| 587 | + |
| 588 | + supervision%command_count = supervision%command_count + 1 |
| 589 | + supervision%last_exit_code = command_result%exit_code |
| 590 | + |
| 591 | + if (command_result%succeeded) return |
| 592 | + |
| 593 | + should_continue = .false. |
| 594 | + supervision%failed_command_kind = command_result%kind |
| 595 | + supervision%process_error_code = command_result%process_error_code |
| 596 | + supervision%timed_out = command_result%timed_out |
| 597 | + end subroutine record_supervised_command |
| 598 | + |
| 365 | 599 | subroutine normalize_options(options) |
| 366 | 600 | type(devloop_options), intent(inout) :: options |
| 367 | 601 | |