Skip to content

Commit 06e2083

Browse files
authored
Merge pull request #968 from rust-lang/wasm-cdylib
Guide the user to using #[crate_type="cdylib"] when using Wasm.
2 parents 778f266 + bf603e8 commit 06e2083

File tree

7 files changed

+76
-23
lines changed

7 files changed

+76
-23
lines changed

tests/spec/features/compilation_targets_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@
107107
end
108108

109109
scenario "compiling to WebAssembly" do
110+
editor.set ['#![crate_type = "bin"]', code].join("\n")
111+
110112
in_build_menu { click_on("Wasm") }
111113

112114
within(:output, :code) do
@@ -115,6 +117,21 @@
115117
end
116118
end
117119

120+
scenario "compiling a library to WebAssembly" do
121+
editor.set <<~EOF
122+
#[no_mangle]
123+
pub fn calculator(a: u8) -> u8 { a + 42 }
124+
EOF
125+
126+
in_build_menu { click_on("Wasm") }
127+
128+
within(:output, :code) do
129+
expect(page).to have_content '(func $calculator (export "calculator")'
130+
end
131+
132+
expect(editor).to have_line('#![crate_type = "cdylib"]')
133+
end
134+
118135
context "when the code doesn't compile" do
119136
before { editor.set("fn main() {") }
120137

ui/frontend/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ module.exports = {
6262
{
6363
files: [
6464
'.eslintrc.js',
65+
'BuildMenu.tsx',
6566
'PopButton.tsx',
6667
'compileActions.ts',
6768
'editor/AceEditor.tsx',

ui/frontend/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ node_modules
1111

1212
# Slowly migrate files that we've touched
1313
!.eslintrc.js
14+
!BuildMenu.tsx
1415
!PopButton.tsx
1516
!compileActions.ts
1617
!editor/AceEditor.tsx

ui/frontend/BuildMenu.tsx

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import React, { useCallback } from 'react';
22
import { useSelector } from 'react-redux';
33

4-
import * as actions from './actions';
5-
import * as selectors from './selectors';
6-
import { useAppDispatch } from './configureStore';
7-
84
import ButtonMenuItem from './ButtonMenuItem';
9-
import MenuGroup from './MenuGroup';
105
import MenuAside from './MenuAside';
6+
import MenuGroup from './MenuGroup';
7+
import * as actions from './actions';
8+
import { useAppDispatch } from './configureStore';
9+
import * as selectors from './selectors';
1110

1211
import styles from './BuildMenu.module.css';
1312

@@ -18,17 +17,15 @@ interface BuildMenuProps {
1817
const useDispatchAndClose = (action: () => actions.ThunkAction, close: () => void) => {
1918
const dispatch = useAppDispatch();
2019

21-
return useCallback(
22-
() => {
23-
dispatch(action());
24-
close();
25-
},
26-
[action, close, dispatch]
27-
);
28-
}
20+
return useCallback(() => {
21+
dispatch(action());
22+
close();
23+
}, [action, close, dispatch]);
24+
};
2925

30-
const BuildMenu: React.FC<BuildMenuProps> = props => {
26+
const BuildMenu: React.FC<BuildMenuProps> = (props) => {
3127
const isHirAvailable = useSelector(selectors.isHirAvailable);
28+
const wasmLikelyToWork = useSelector(selectors.wasmLikelyToWork);
3229

3330
const compile = useDispatchAndClose(actions.performCompile, props.close);
3431
const compileToAssembly = useDispatchAndClose(actions.performCompileToAssembly, props.close);
@@ -42,16 +39,13 @@ const BuildMenu: React.FC<BuildMenuProps> = props => {
4239
return (
4340
<MenuGroup title="What do you want to do?">
4441
<ButtonMenuItem name="Run" onClick={execute}>
45-
Build and run the code, showing the output.
46-
Equivalent to <code className={styles.code}>cargo run</code>.
42+
Build and run the code, showing the output. Equivalent to <Code>cargo run</Code>.
4743
</ButtonMenuItem>
4844
<ButtonMenuItem name="Build" onClick={compile}>
49-
Build the code without running it.
50-
Equivalent to <code className={styles.code}>cargo build</code>.
45+
Build the code without running it. Equivalent to <Code>cargo build</Code>.
5146
</ButtonMenuItem>
5247
<ButtonMenuItem name="Test" onClick={test}>
53-
Build the code and run all the tests.
54-
Equivalent to <code className={styles.code}>cargo test</code>.
48+
Build the code and run all the tests. Equivalent to <Code>cargo test</Code>.
5549
</ButtonMenuItem>
5650
<ButtonMenuItem name="ASM" onClick={compileToAssembly}>
5751
Build and show the resulting assembly code.
@@ -68,15 +62,28 @@ const BuildMenu: React.FC<BuildMenuProps> = props => {
6862
</ButtonMenuItem>
6963
<ButtonMenuItem name="Wasm" onClick={compileToWasm}>
7064
Build a WebAssembly module for web browsers, in the .WAT textual representation.
65+
{!wasmLikelyToWork && <WasmAside />}
7166
</ButtonMenuItem>
7267
</MenuGroup>
7368
);
7469
};
7570

71+
const Code: React.FC<{ children: string }> = ({ children }) => (
72+
<code className={styles.code}>{children}</code>
73+
);
74+
7675
const HirAside: React.FC = () => (
7776
<MenuAside>
78-
Note: HIR currently requires using the Nightly channel, selecting this
79-
option will switch to Nightly.
77+
Note: HIR currently requires using the Nightly channel, selecting this option will switch to
78+
Nightly.
79+
</MenuAside>
80+
);
81+
82+
const WasmAside: React.FC = () => (
83+
<MenuAside>
84+
Note: WebAssembly works best when using the <Code>cdylib</Code> crate type, but the source code
85+
does not specify an explicit crate type. Selecting this option will change the code to specify{' '}
86+
<Code>cdylib</Code>.
8087
</MenuAside>
8188
);
8289

ui/frontend/actions.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
clippyRequestSelector,
77
getCrateType,
88
runAsTest,
9+
wasmLikelyToWork,
910
} from './selectors';
1011
import State from './state';
1112
import {
@@ -89,6 +90,7 @@ export enum ActionType {
8990
EditCode = 'EDIT_CODE',
9091
AddMainFunction = 'ADD_MAIN_FUNCTION',
9192
AddImport = 'ADD_IMPORT',
93+
AddCrateType = 'ADD_CRATE_TYPE',
9294
EnableFeatureGate = 'ENABLE_FEATURE_GATE',
9395
GotoPosition = 'GOTO_POSITION',
9496
SelectText = 'SELECT_TEXT',
@@ -272,6 +274,15 @@ const performCompileToNightlyHirOnly = (): ThunkAction => dispatch => {
272274
dispatch(performCompileToHirOnly());
273275
};
274276

277+
const performCompileToCdylibWasmOnly = (): ThunkAction => (dispatch, getState) => {
278+
const state = getState();
279+
280+
if (!wasmLikelyToWork(state)) {
281+
dispatch(addCrateType('cdylib'));
282+
}
283+
dispatch(performCompileToWasmOnly());
284+
};
285+
275286
const PRIMARY_ACTIONS: { [index in PrimaryAction]: () => ThunkAction } = {
276287
[PrimaryActionCore.Asm]: performCompileToAssemblyOnly,
277288
[PrimaryActionCore.Compile]: performCompileOnly,
@@ -310,7 +321,7 @@ export const performCompileToMir =
310321
export const performCompileToNightlyHir =
311322
performAndSwitchPrimaryAction(performCompileToNightlyHirOnly, PrimaryActionCore.Hir);
312323
export const performCompileToWasm =
313-
performAndSwitchPrimaryAction(performCompileToWasmOnly, PrimaryActionCore.Wasm);
324+
performAndSwitchPrimaryAction(performCompileToCdylibWasmOnly, PrimaryActionCore.Wasm);
314325

315326
export const editCode = (code: string) =>
316327
createAction(ActionType.EditCode, { code });
@@ -321,6 +332,9 @@ export const addMainFunction = () =>
321332
export const addImport = (code: string) =>
322333
createAction(ActionType.AddImport, { code });
323334

335+
export const addCrateType = (crateType: string) =>
336+
createAction(ActionType.AddCrateType, { crateType });
337+
324338
export const enableFeatureGate = (featureGate: string) =>
325339
createAction(ActionType.EnableFeatureGate, { featureGate });
326340

@@ -617,6 +631,7 @@ export type Action =
617631
| ReturnType<typeof editCode>
618632
| ReturnType<typeof addMainFunction>
619633
| ReturnType<typeof addImport>
634+
| ReturnType<typeof addCrateType>
620635
| ReturnType<typeof enableFeatureGate>
621636
| ReturnType<typeof gotoPosition>
622637
| ReturnType<typeof selectText>

ui/frontend/reducers/code.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export default function code(state = DEFAULT, action: Action): State {
1919
case ActionType.AddImport:
2020
return action.code + state;
2121

22+
case ActionType.AddCrateType:
23+
return `#![crate_type = "${action.crateType}"]\n${state}`;
24+
2225
case ActionType.EnableFeatureGate:
2326
return `#![feature(${action.featureGate})]\n${state}`;
2427

ui/frontend/selectors/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ export const isNightlyChannel = (state: State) => (
127127
);
128128
export const isHirAvailable = isNightlyChannel;
129129

130+
export const wasmLikelyToWork = createSelector(
131+
crateTypeSelector,
132+
getCrateType, (userCrateType, crateType) => {
133+
// If the user set it already, assume they know what they are doing
134+
if (userCrateType) { return true }
135+
136+
return crateType === 'cdylib';
137+
});
138+
130139
export const getModeLabel = (state: State) => {
131140
const { configuration: { mode } } = state;
132141
return `${mode}`;

0 commit comments

Comments
 (0)