Skip to content

Commit

Permalink
Add build instrumentation handling logic
Browse files Browse the repository at this point in the history
  • Loading branch information
williamjallen committed Dec 3, 2024
1 parent 2cbcf6b commit 59f0bbc
Show file tree
Hide file tree
Showing 16 changed files with 888 additions and 138 deletions.
11 changes: 11 additions & 0 deletions app/Enums/BuildCommandType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace App\Enums;

enum BuildCommandType: int
{
case COMPILE_COMMAND = 0;
case LINK_COMMAND = 1;
case CMAKE_BUILD_COMMAND = 2;
case CUSTOM_COMMAND = 3;
}
8 changes: 0 additions & 8 deletions app/Enums/BuildMeasurementType.php

This file was deleted.

49 changes: 41 additions & 8 deletions app/Models/Build.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Models;

use App\Enums\BuildCommandType;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
Expand Down Expand Up @@ -214,14 +215,6 @@ public function buildGroups(): BelongsToMany
return $this->belongsToMany(BuildGroup::class, 'build2group', 'groupid', 'buildid');
}

/**
* @return HasMany<BuildMeasurement>
*/
public function measurements(): HasMany
{
return $this->hasMany(BuildMeasurement::class, 'buildid');
}

/**
* @return HasMany<Coverage>
*/
Expand All @@ -237,4 +230,44 @@ public function labels(): BelongsToMany
{
return $this->belongsToMany(Label::class, 'label2build', 'buildid', 'labelid');
}

/**
* @return HasMany<BuildCommand>
*/
public function commands(): HasMany
{
return $this->hasMany(BuildCommand::class, 'buildid');
}

/**
* @return HasMany<BuildCommand>
*/
public function compileCommands(): HasMany
{
return $this->commands()->where('type', BuildCommandType::COMPILE_COMMAND);
}

/**
* @return HasMany<BuildCommand>
*/
public function linkCommands(): HasMany
{
return $this->commands()->where('type', BuildCommandType::LINK_COMMAND);
}

/**
* @return HasMany<BuildCommand>
*/
public function cmakeBuildCommands(): HasMany
{
return $this->commands()->where('type', BuildCommandType::CMAKE_BUILD_COMMAND);
}

/**
* @return HasMany<BuildCommand>
*/
public function customCommands(): HasMany
{
return $this->commands()->where('type', BuildCommandType::CUSTOM_COMMAND);
}
}
72 changes: 72 additions & 0 deletions app/Models/BuildCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace App\Models;

use App\Enums\BuildCommandType;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
* @property int $id
* @property int $buildid
* @property BuildCommandType $type
* @property Carbon $starttime
* @property Carbon $endtime
* @property string $command
* @property string $binarydirectory
* @property string $returnvalue
* @property string $output
* @property string $language
* @property string $target
* @property string $targettype
* @property string $source
*
* @mixin Builder<BuildCommand>
*/
class BuildCommand extends Model
{
protected $table = 'buildcommands';

public $timestamps = false;

protected $fillable = [
'type',
'starttime',
'endtime',
'command',
'binarydirectory',
'returnvalue',
'output',
'language',
'target',
'targettype',
'source',
];

protected $casts = [
'id' => 'integer',
'buildid' => 'integer',
'type' => BuildCommandType::class,
'starttime' => 'datetime',
'endtime' => 'datetime',
];

/**
* @return BelongsTo<Build, self>
*/
public function build(): BelongsTo
{
return $this->belongsTo(Build::class, 'buildid');
}

/**
* @return HasMany<BuildMeasurement>
*/
public function measurements(): HasMany
{
return $this->hasMany(BuildMeasurement::class, 'buildcommandid');
}
}
19 changes: 3 additions & 16 deletions app/Models/BuildMeasurement.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@

namespace App\Models;

use App\Enums\BuildMeasurementType;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;

/**
* @property int $id
* @property int $buildid
* @property int $buildcommandid
* @property string $name
* @property string $source
* @property BuildMeasurementType $type
* @property string $type
* @property string $value
*
* @mixin Builder<BuildMeasurement>
Expand All @@ -25,22 +22,12 @@ class BuildMeasurement extends Model

protected $fillable = [
'name',
'source',
'type',
'value',
];

protected $casts = [
'id' => 'integer',
'buildid' => 'integer',
'type' => BuildMeasurementType::class,
'buildcommandid' => 'integer',
];

/**
* @return HasOne<Build>
*/
public function build(): HasOne
{
return $this->hasOne(Build::class, 'id', 'buildid');
}
}
6 changes: 0 additions & 6 deletions app/Providers/GraphQLServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@

namespace App\Providers;

use App\Enums\BuildMeasurementType;
use Illuminate\Support\ServiceProvider;
use Nuwave\Lighthouse\Schema\TypeRegistry;
use GraphQL\Type\Definition\PhpEnumType;

final class GraphQLServiceProvider extends ServiceProvider
{
/**
* @throws \GraphQL\Error\InvariantViolation
*/
public function boot(TypeRegistry $typeRegistry): void
{
$typeRegistry->register(new PhpEnumType(BuildMeasurementType::class));
}
}
15 changes: 14 additions & 1 deletion app/cdash/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,24 @@ set_tests_properties(/Feature/GraphQL/NoteTypeTest PROPERTIES DEPENDS /Feature/G
add_laravel_test(/Feature/GraphQL/BuildMeasurementTypeTest)
set_tests_properties(/Feature/GraphQL/BuildMeasurementTypeTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest)

add_laravel_test(/Feature/GraphQL/CompileBuildCommandTypeTest)
set_tests_properties(/Feature/GraphQL/CompileBuildCommandTypeTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest)

add_laravel_test(/Feature/GraphQL/CoverageTypeTest)
set_tests_properties(/Feature/GraphQL/CoverageTypeTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest)

add_laravel_test(/Feature/PurgeUnusedProjectsCommand)
set_tests_properties(/Feature/PurgeUnusedProjectsCommand PROPERTIES DEPENDS "/Feature/GraphQL/TestTypeTest;/Feature/GraphQL/TestMeasurementTypeTest;/Feature/GraphQL/NoteTypeTest;/Feature/GraphQL/BuildMeasurementTypeTest;/Feature/GraphQL/CoverageTypeTest")
set_property(TEST /Feature/PurgeUnusedProjectsCommand PROPERTY DEPENDS
/Feature/GraphQL/TestTypeTest
/Feature/GraphQL/TestMeasurementTypeTest
/Feature/GraphQL/NoteTypeTest
/Feature/GraphQL/BuildMeasurementTypeTest
/Feature/GraphQL/CoverageTypeTest
/Feature/GraphQL/CompileBuildCommandTypeTest
/Feature/GraphQL/LinkBuildCommandTypeTest
/Feature/GraphQL/CMakeBuildCommandTypeTest
/Feature/GraphQL/CustomBuildCommandTypeTest
)

add_laravel_test(/Feature/MeasurementPositionMigration)
set_tests_properties(/Feature/MeasurementPositionMigration PROPERTIES DEPENDS /Feature/PurgeUnusedProjectsCommand)
Expand Down
75 changes: 75 additions & 0 deletions app/cdash/xml_handlers/build_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
PURPOSE. See the above copyright notices for more information.
=========================================================================*/

use App\Enums\BuildCommandType;
use App\Models\Build as EloquentBuild;
use App\Models\BuildCommand;
use App\Models\BuildMeasurement;
use App\Utils\SubmissionUtils;
use Carbon\Carbon;
use CDash\Collection\BuildCollection;
use CDash\Collection\SubscriptionBuilderCollection;
use CDash\Messaging\Subscription\CommitAuthorSubscriptionBuilder;
Expand Down Expand Up @@ -56,6 +61,18 @@ class BuildHandler extends AbstractXmlHandler implements ActionableBuildInterfac
private $PullRequest;
private $BuildErrorFilter;

/**
* A list of BuildCommands to be attached to the parent build and associated
* unsaved measurements. This should eventually be turned into a mapping of
* subproject names to BuildCommands so BuildCommands can be associated with
* child builds.
*
* @var array<BuildCommand>
*/
private array $BuildCommands = [];

private BuildMeasurement $MostRecentBuildMeasurement;

public function __construct(Project $project)
{
parent::__construct($project);
Expand Down Expand Up @@ -183,6 +200,46 @@ public function startElement($parser, $name, $attributes): void
$this->ErrorSubProjectName = "";
} elseif ($name == 'LABEL') {
$this->Label = $factory->create(Label::class);
} elseif ($this->getParent() === 'COMMANDS') {
$command_info = [
'starttime' => Carbon::createFromTimestamp($attributes['TIMESTART']),
'endtime' => Carbon::createFromTimestamp($attributes['TIMEEND']),
'command' => $attributes['COMMAND'],
'binarydirectory' => $attributes['BINARYDIR'],
];

switch ($name) {
case 'COMPILE':
$command_info['type'] = BuildCommandType::COMPILE_COMMAND;
$command_info['language'] = $attributes['LANGUAGE'];
$command_info['output'] = $attributes['OUTPUT'];
$command_info['source'] = $attributes['SOURCE'];
$command_info['target'] = $attributes['TARGET'];
break;
case 'LINK':
$command_info['type'] = BuildCommandType::LINK_COMMAND;
$command_info['language'] = $attributes['LANGUAGE'];
$command_info['output'] = $attributes['OUTPUT'];
$command_info['target'] = $attributes['TARGET'];
$command_info['targettype'] = $attributes['TARGETTYPE'];
break;
case 'CMAKEBUILD':
$command_info['type'] = BuildCommandType::CMAKE_BUILD_COMMAND;
break;
case 'CUSTOM':
$command_info['type'] = BuildCommandType::CUSTOM_COMMAND;
// TODO: Finish this
break;
default:
throw new Exception("Unknown element $name");
}

$this->BuildCommands[] = new BuildCommand($command_info);
} elseif ($name === 'NAMEDMEASUREMENT') {
$this->MostRecentBuildMeasurement = new BuildMeasurement([
'type' => $attributes['TYPE'],
'name' => $attributes['NAME'],
]);
}
}

Expand Down Expand Up @@ -232,6 +289,21 @@ public function endElement($parser, $name): void

$build->ComputeDifferences();
}

if (count($this->BuildCommands) > 0) {
// This is an unfortunate side effect of not having the build until the end.
// In the future, it would be good to initialize the build earlier and directly
// attach children as we go.
$eloquent_parent_build = EloquentBuild::findOrFail((int) $this->getBuild()->Id);
foreach ($this->BuildCommands as $command) {
$build_command = $eloquent_parent_build->commands()->save($command);
if ($build_command === false) {
throw new Exception('Build command failed to save.');
}
$build_command->measurements()->saveMany($command->measurements);
}
$eloquent_parent_build->push();
}
} elseif ($name == 'WARNING' || $name == 'ERROR' || $name == 'FAILURE') {
$skip_error = false;
foreach (['StdOutput', 'StdError', 'Text'] as $field) {
Expand Down Expand Up @@ -391,6 +463,9 @@ public function text($parser, $data)
if (empty($this->ErrorSubProjectName)) {
$this->Label->SetText($data);
}
} elseif ($element === 'VALUE' && $parent === 'NAMEDMEASUREMENT') {
$this->MostRecentBuildMeasurement->value = $data;
$this->BuildCommands[array_key_last($this->BuildCommands)]->measurements->add($this->MostRecentBuildMeasurement);
}
}

Expand Down
Loading

0 comments on commit 59f0bbc

Please sign in to comment.