Repository: compiler-explorer/compiler-explorer
Pull request #7141 (2024-11-28 12:17:45)

Allow passing --help to compilers
As per https://github.com/compiler-explorer/compiler-explorer/issues/6199#issuecomment-2505980377, allowing passing `--help` to compilers so that users can know what options and switches are available (though part of them will be filtered out, but still better than having nothing). 


Patch:
diff --git a/app.ts b/app.ts
index d466b6a17..d8923c376 100755
--- a/app.ts
+++ b/app.ts
@@ -57,6 +57,7 @@ import {startWineInit} from './lib/exec.js';
 import {RemoteExecutionQuery} from './lib/execution/execution-query.js';
 import {initHostSpecialties} from './lib/execution/execution-triple.js';
 import {startExecutionWorkerThread} from './lib/execution/sqs-execution-queue.js';
+import {SiteTemplateController} from './lib/handlers/api/site-template-controller.js';
 import {SourceController} from './lib/handlers/api/source-controller.js';
 import {CompileHandler} from './lib/handlers/compile.js';
 import * as healthCheck from './lib/handlers/health-check.js';
@@ -557,6 +558,8 @@ async function main() {
     const sourceHandler = new SourceController(sources);
     const compilerFinder = new CompilerFinder(compileHandler, compilerProps, awsProps, defArgs, clientOptionsHandler);
 
+    const siteTemplateController = new SiteTemplateController();
+
     logger.info('=======================================');
     if (gitReleaseName) logger.info(`  git release ${gitReleaseName}`);
     if (releaseBuildNumber) logger.info(`  release build ${releaseBuildNumber}`);
@@ -869,6 +872,7 @@ async function main() {
         .use(bodyParser.json({limit: ceProps('bodyParserLimit', maxUploadSize)}))
         .use('/source/:source/list', sourceHandler.listEntries.bind(sourceHandler))
         .use('/source/:source/load/:language/:filename', sourceHandler.loadEntry.bind(sourceHandler))
+        .get('/api/siteTemplates', siteTemplateController.getSiteTemplates.bind(siteTemplateController))
         .get('/g/:id', oldGoogleUrlHandler)
         // Deprecated old route for this -- TODO remove in late 2021
         .post('/shortener', routeApi.apiHandler.shortener.handle.bind(routeApi.apiHandler.shortener));
diff --git a/lib/base-compiler.ts b/lib/base-compiler.ts
index 830c9f486..1d492d84e 100644
--- a/lib/base-compiler.ts
+++ b/lib/base-compiler.ts
@@ -180,6 +180,10 @@ export interface SimpleOutputFilenameCompiler {
     getOutputFilename(dirPath: string): string;
 }
 
+function isOutputLikelyLllvmIr(compilerOptions) {
+    return compilerOptions && (compilerOptions.includes('-emit-llvm') || compilerOptions.includes('-mlir-to-llvmir'));
+}
+
 export class BaseCompiler {
     public compiler: CompilerInfo;
     public lang: Language;
@@ -3063,7 +3067,7 @@ export class BaseCompiler {
             if (this.compiler.supportsCfg && backendOptions.produceCfg && backendOptions.produceCfg.asm) {
                 const isLlvmIr =
                     this.compiler.instructionSet === 'llvm' ||
-                    (options && options.includes('-emit-llvm')) ||
+                    (options && isOutputLikelyLllvmIr(options)) ||
                     this.llvmIr.isLlvmIr(result.asm);
                 result.cfg = cfg.generateStructure(this.compiler, result.asm, isLlvmIr);
             }
@@ -3114,7 +3118,7 @@ export class BaseCompiler {
     }
 
     async processAsm(result, filters: ParseFiltersAndOutputOptions, options: string[]) {
-        if ((options && options.includes('-emit-llvm')) || this.llvmIr.isLlvmIr(result.asm)) {
+        if ((options && isOutputLikelyLllvmIr(options)) || this.llvmIr.isLlvmIr(result.asm)) {
             return await this.llvmIr.processFromFilters(result.asm, filters);
         }
 
diff --git a/lib/compilers/dotnet.ts b/lib/compilers/dotnet.ts
index 9b48d9a12..226bf565b 100644
--- a/lib/compilers/dotnet.ts
+++ b/lib/compilers/dotnet.ts
@@ -173,6 +173,7 @@ class DotNetCompiler extends BaseCompiler {
             '--noscan',
             '--noinlinetls',
             '--completetypemetadata',
+            '--help',
             '-bytes',
             '-raweh',
             '-tokens',
diff --git a/lib/compilers/mlir.ts b/lib/compilers/mlir.ts
index 1e20aa737..20936def8 100644
--- a/lib/compilers/mlir.ts
+++ b/lib/compilers/mlir.ts
@@ -31,6 +31,10 @@ import {CompilationEnvironment} from '../compilation-env.js';
 
 import {BaseParser} from './argument-parsers.js';
 
+function isMlirTranslate(compilerInfo: PreliminaryCompilerInfo): boolean {
+    return compilerInfo.group === 'mlirtranslate';
+}
+
 export class MLIRCompiler extends BaseCompiler {
     static get key() {
         return 'mlir';
@@ -39,18 +43,20 @@ export class MLIRCompiler extends BaseCompiler {
     constructor(compilerInfo: PreliminaryCompilerInfo, env: CompilationEnvironment) {
         super(
             {
-                disabledFilters: [
-                    'binary',
-                    'execute',
-                    'demangle',
-                    'intel',
-                    'labels',
-                    'libraryCode',
-                    'directives',
-                    'commentOnly',
-                    'trim',
-                    'debugCalls',
-                ],
+                disabledFilters: isMlirTranslate(compilerInfo)
+                    ? []
+                    : [
+                          'binary',
+                          'execute',
+                          'demangle',
+                          'intel',
+                          'labels',
+                          'libraryCode',
+                          'directives',
+                          'commentOnly',
+                          'trim',
+                          'debugCalls',
+                      ],
                 ...compilerInfo,
             },
             env,
@@ -85,20 +91,22 @@ export class MLIRCompiler extends BaseCompiler {
         // at some point maybe a custom parser can be written, for now just don't filter anything
         return super.processAsm(
             result,
-            {
-                labels: false,
-                binary: false,
-                commentOnly: false,
-                demangle: false,
-                optOutput: false,
-                directives: false,
-                dontMaskFilenames: false,
-                execute: false,
-                intel: false,
-                libraryCode: false,
-                trim: false,
-                debugCalls: false,
-            },
+            isMlirTranslate(this.compiler)
+                ? filters
+                : {
+                      labels: false,
+                      binary: false,
+                      commentOnly: false,
+                      demangle: false,
+                      optOutput: false,
+                      directives: false,
+                      dontMaskFilenames: false,
+                      execute: false,
+                      intel: false,
+                      libraryCode: false,
+                      trim: false,
+                      debugCalls: false,
+                  },
             options,
         );
     }
diff --git a/lib/handlers/api.ts b/lib/handlers/api.ts
index a90b3a228..735eefc55 100644
--- a/lib/handlers/api.ts
+++ b/lib/handlers/api.ts
@@ -44,7 +44,6 @@ import {StorageBase} from '../storage/index.js';
 import {withAssemblyDocumentationProviders} from './assembly-documentation.js';
 import {CompileHandler} from './compile.js';
 import {FormattingHandler} from './formatting.js';
-import {getSiteTemplates} from './site-templates.js';
 
 function methodNotAllowed(req: express.Request, res: express.Response) {
     res.send('Method Not Allowed');
@@ -139,13 +138,6 @@ export class ApiHandler {
                 res.send(all);
             })
             .all(methodNotAllowed);
-        this.handle
-            .route('/siteTemplates')
-            .get(async (req, res) => {
-                res.send(await getSiteTemplates());
-            })
-            .all(methodNotAllowed);
-
         this.handle.route('/shortlinkinfo/:id').get(this.shortlinkInfoHandler.bind(this)).all(methodNotAllowed);
 
         const shortenerType = getShortenerTypeByKey(urlShortenService);
diff --git a/lib/handlers/api/site-template-controller.ts b/lib/handlers/api/site-template-controller.ts
new file mode 100644
index 000000000..6f49a9732
--- /dev/null
+++ b/lib/handlers/api/site-template-controller.ts
@@ -0,0 +1,34 @@
+// Copyright (c) 2024, Compiler Explorer Authors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+import express from 'express';
+
+import {getSiteTemplates} from '../site-templates.js';
+
+export class SiteTemplateController {
+    public async getSiteTemplates(req: express.Request, res: express.Response) {
+        const templates = await getSiteTemplates();
+        res.send(templates);
+    }
+}


Test patch:
diff --git a/test/handlers/api-tests.ts b/test/handlers/api-tests.ts
index e809a25f6..92194b165 100644
--- a/test/handlers/api-tests.ts
+++ b/test/handlers/api-tests.ts
@@ -183,7 +183,4 @@ describe('API handling', () => {
             .expect(422, {exit: 3, answer: "Style 'bad-base' is not supported"})
             .expect('Content-Type', /json/);
     });
-    it('should respond to plain site template requests', async () => {
-        await request(app).get('/api/siteTemplates').expect(200).expect('Content-Type', /json/);
-    });
 });
diff --git a/test/handlers/site-templates-tests.ts b/test/handlers/site-templates-tests.ts
index e9d0fa112..aca4dc2ef 100644
--- a/test/handlers/site-templates-tests.ts
+++ b/test/handlers/site-templates-tests.ts
@@ -1,12 +1,26 @@
-import {describe, expect, it} from 'vitest';
+import express from 'express';
+import request from 'supertest';
+import {beforeAll, describe, expect, it} from 'vitest';
 
+import {SiteTemplateController} from '../../lib/handlers/api/site-template-controller.js';
 import {getSiteTemplates} from '../../lib/handlers/site-templates.js';
 
 describe('Site Templates Backend', () => {
+    let app: express.Express;
+    beforeAll(() => {
+        app = express();
+        const controller = new SiteTemplateController();
+        app.use('/api/siteTemplates', controller.getSiteTemplates.bind(controller));
+    });
+
     it('should load site templates properly', async () => {
         const templates = await getSiteTemplates();
         // not super comprehensive
         expect(templates.meta).toHaveProperty('meta.screenshot_dimensions');
         expect(Object.entries(templates.templates).length).toBeTruthy();
     });
+
+    it('should respond to plain site template requests', async () => {
+        await request(app).get('/api/siteTemplates').expect(200).expect('Content-Type', /json/);
+    });
 });