forked from bazelbuild/bazel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simplify RuleClassProviders by making most RuleSets have their own cl…
…ass. PiperOrigin-RevId: 184540561
- Loading branch information
Showing
10 changed files
with
446 additions
and
303 deletions.
There are no files selected for viewing
308 changes: 12 additions & 296 deletions
308
src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
Large diffs are not rendered by default.
Oops, something went wrong.
80 changes: 80 additions & 0 deletions
80
src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// Copyright 2018 The Bazel Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package com.google.devtools.build.lib.bazel.rules; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.Builder; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.RuleSet; | ||
import com.google.devtools.build.lib.bazel.rules.CcToolchainType.CcToolchainTypeRule; | ||
import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcBinaryRule; | ||
import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcImportRule; | ||
import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcIncLibraryRule; | ||
import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcLibraryRule; | ||
import com.google.devtools.build.lib.bazel.rules.cpp.BazelCcTestRule; | ||
import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses; | ||
import com.google.devtools.build.lib.rules.core.CoreRules; | ||
import com.google.devtools.build.lib.rules.cpp.CcImportRule; | ||
import com.google.devtools.build.lib.rules.cpp.CcIncLibraryRule; | ||
import com.google.devtools.build.lib.rules.cpp.CcModule; | ||
import com.google.devtools.build.lib.rules.cpp.CcToolchainAlias.CcToolchainAliasRule; | ||
import com.google.devtools.build.lib.rules.cpp.CcToolchainRule; | ||
import com.google.devtools.build.lib.rules.cpp.CcToolchainSuiteRule; | ||
import com.google.devtools.build.lib.rules.cpp.CppBuildInfo; | ||
import com.google.devtools.build.lib.rules.cpp.CppConfigurationLoader; | ||
import com.google.devtools.build.lib.rules.cpp.CppOptions; | ||
import com.google.devtools.build.lib.rules.cpp.CpuTransformer; | ||
import com.google.devtools.build.lib.rules.platform.PlatformRules; | ||
|
||
/** | ||
* Rules for C++ support in Bazel. | ||
*/ | ||
public class CcRules implements RuleSet { | ||
public static final CcRules INSTANCE = new CcRules(); | ||
|
||
private CcRules() { | ||
// Use the static INSTANCE field instead. | ||
} | ||
|
||
@Override | ||
public void init(Builder builder) { | ||
builder.addSkylarkAccessibleTopLevels("cc_common", CcModule.INSTANCE); | ||
|
||
builder.addConfig(CppOptions.class, new CppConfigurationLoader(CpuTransformer.IDENTITY)); | ||
builder.addBuildInfoFactory(new CppBuildInfo()); | ||
|
||
builder.addRuleDefinition(new CcToolchainRule()); | ||
builder.addRuleDefinition(new CcToolchainSuiteRule()); | ||
builder.addRuleDefinition(new CcToolchainAliasRule()); | ||
builder.addRuleDefinition(new CcIncLibraryRule()); | ||
builder.addRuleDefinition(new CcImportRule()); | ||
builder.addRuleDefinition(new CcToolchainTypeRule()); | ||
builder.addRuleDefinition(new BazelCppRuleClasses.CcLinkingRule()); | ||
builder.addRuleDefinition(new BazelCppRuleClasses.CcDeclRule()); | ||
builder.addRuleDefinition(new BazelCppRuleClasses.CcBaseRule()); | ||
builder.addRuleDefinition(new BazelCppRuleClasses.CcRule( | ||
BazelRuleClassProvider.TOOLS_REPOSITORY + "//tools/def_parser:def_parser")); | ||
builder.addRuleDefinition(new BazelCppRuleClasses.CcBinaryBaseRule()); | ||
builder.addRuleDefinition(new BazelCcBinaryRule()); | ||
builder.addRuleDefinition(new BazelCcTestRule()); | ||
builder.addRuleDefinition(new BazelCppRuleClasses.CcLibraryBaseRule()); | ||
builder.addRuleDefinition(new BazelCcLibraryRule()); | ||
builder.addRuleDefinition(new BazelCcIncLibraryRule()); | ||
builder.addRuleDefinition(new BazelCcImportRule()); | ||
} | ||
|
||
@Override | ||
public ImmutableList<RuleSet> requires() { | ||
return ImmutableList.of(CoreRules.INSTANCE, PlatformRules.INSTANCE); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
src/main/java/com/google/devtools/build/lib/bazel/rules/GenericRules.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright 2018 The Bazel Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package com.google.devtools.build.lib.bazel.rules; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.Builder; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.RuleSet; | ||
import com.google.devtools.build.lib.analysis.constraints.EnvironmentRule; | ||
import com.google.devtools.build.lib.bazel.rules.common.BazelFilegroupRule; | ||
import com.google.devtools.build.lib.rules.Alias.AliasRule; | ||
import com.google.devtools.build.lib.rules.core.CoreRules; | ||
import com.google.devtools.build.lib.rules.genquery.GenQueryRule; | ||
import com.google.devtools.build.lib.rules.test.TestSuiteRule; | ||
import com.google.devtools.build.lib.util.ResourceFileLoader; | ||
import java.io.IOException; | ||
|
||
/** | ||
* A set of generic rules that provide miscellaneous capabilities to Bazel. | ||
*/ | ||
public class GenericRules implements RuleSet { | ||
public static final GenericRules INSTANCE = new GenericRules(); | ||
|
||
private GenericRules() { | ||
// Use the static INSTANCE field instead. | ||
} | ||
|
||
@Override | ||
public void init(Builder builder) { | ||
builder.addRuleDefinition(new EnvironmentRule()); | ||
|
||
builder.addRuleDefinition(new AliasRule()); | ||
builder.addRuleDefinition(new BazelFilegroupRule()); | ||
builder.addRuleDefinition(new TestSuiteRule()); | ||
builder.addRuleDefinition(new GenQueryRule()); | ||
|
||
try { | ||
builder.addWorkspaceFilePrefix( | ||
ResourceFileLoader.loadResource(BazelRuleClassProvider.class, "tools.WORKSPACE") | ||
// Hackily select the java_toolchain based on the host JDK version. JDK 8 and | ||
// 9 host_javabases require different toolchains, e.g. to use --patch-module | ||
// instead of -Xbootclasspath/p:. | ||
.replace( | ||
"%java_toolchain%", | ||
isJdk8OrEarlier() | ||
? "@bazel_tools//tools/jdk:toolchain_jdk8" | ||
: "@bazel_tools//tools/jdk:toolchain_jdk9")); | ||
|
||
} catch (IOException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public ImmutableList<RuleSet> requires() { | ||
return ImmutableList.of(CoreRules.INSTANCE); | ||
} | ||
|
||
private static boolean isJdk8OrEarlier() { | ||
return Double.parseDouble(System.getProperty("java.class.version")) <= 52.0; | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
src/main/java/com/google/devtools/build/lib/bazel/rules/JavaRules.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Copyright 2018 The Bazel Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package com.google.devtools.build.lib.bazel.rules; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.Builder; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.RuleSet; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaBinaryRule; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaBuildInfoFactory; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaImportRule; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaLibraryRule; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaPluginRule; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaRuleClasses; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaSemantics; | ||
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaTestRule; | ||
import com.google.devtools.build.lib.rules.core.CoreRules; | ||
import com.google.devtools.build.lib.rules.extra.ActionListenerRule; | ||
import com.google.devtools.build.lib.rules.extra.ExtraActionRule; | ||
import com.google.devtools.build.lib.rules.java.JavaConfigurationLoader; | ||
import com.google.devtools.build.lib.rules.java.JavaImportBaseRule; | ||
import com.google.devtools.build.lib.rules.java.JavaInfo; | ||
import com.google.devtools.build.lib.rules.java.JavaOptions; | ||
import com.google.devtools.build.lib.rules.java.JavaPackageConfigurationRule; | ||
import com.google.devtools.build.lib.rules.java.JavaRuleClasses.IjarBaseRule; | ||
import com.google.devtools.build.lib.rules.java.JavaRuntimeAlias; | ||
import com.google.devtools.build.lib.rules.java.JavaRuntimeRule; | ||
import com.google.devtools.build.lib.rules.java.JavaRuntimeSuiteRule; | ||
import com.google.devtools.build.lib.rules.java.JavaSkylarkCommon; | ||
import com.google.devtools.build.lib.rules.java.JavaToolchainAlias; | ||
import com.google.devtools.build.lib.rules.java.JavaToolchainRule; | ||
import com.google.devtools.build.lib.rules.java.ProguardLibraryRule; | ||
import com.google.devtools.build.lib.rules.java.proto.JavaProtoSkylarkCommon; | ||
import com.google.devtools.build.lib.util.ResourceFileLoader; | ||
import java.io.IOException; | ||
|
||
/** | ||
* Rules for Java support in Bazel. | ||
*/ | ||
public class JavaRules implements RuleSet { | ||
public static final JavaRules INSTANCE = new JavaRules(); | ||
|
||
private JavaRules() { | ||
// Use the static INSTANCE field instead. | ||
} | ||
|
||
@Override | ||
public void init(Builder builder) { | ||
builder.addConfigurationOptions(JavaOptions.class); | ||
builder.addConfigurationFragment(new JavaConfigurationLoader()); | ||
|
||
builder.addBuildInfoFactory(new BazelJavaBuildInfoFactory()); | ||
|
||
builder.addRuleDefinition(new BazelJavaRuleClasses.BaseJavaBinaryRule()); | ||
builder.addRuleDefinition(new IjarBaseRule()); | ||
builder.addRuleDefinition(new BazelJavaRuleClasses.JavaBaseRule()); | ||
builder.addRuleDefinition(new ProguardLibraryRule()); | ||
builder.addRuleDefinition(new JavaImportBaseRule()); | ||
builder.addRuleDefinition(new BazelJavaRuleClasses.JavaRule()); | ||
builder.addRuleDefinition(new BazelJavaBinaryRule()); | ||
builder.addRuleDefinition(new BazelJavaLibraryRule()); | ||
builder.addRuleDefinition(new BazelJavaImportRule()); | ||
builder.addRuleDefinition(new BazelJavaTestRule()); | ||
builder.addRuleDefinition(new BazelJavaPluginRule()); | ||
builder.addRuleDefinition(new JavaToolchainRule()); | ||
builder.addRuleDefinition(new JavaPackageConfigurationRule()); | ||
builder.addRuleDefinition(new JavaRuntimeRule()); | ||
builder.addRuleDefinition(new JavaRuntimeSuiteRule()); | ||
builder.addRuleDefinition(new JavaRuntimeAlias.JavaRuntimeAliasRule()); | ||
builder.addRuleDefinition(new JavaToolchainAlias.JavaToolchainAliasRule()); | ||
|
||
builder.addRuleDefinition(new ExtraActionRule()); | ||
builder.addRuleDefinition(new ActionListenerRule()); | ||
|
||
builder.addSkylarkAccessibleTopLevels("java_common", | ||
new JavaSkylarkCommon(BazelJavaSemantics.INSTANCE)); | ||
builder.addSkylarkAccessibleTopLevels("JavaInfo", JavaInfo.PROVIDER); | ||
builder.addSkylarkAccessibleTopLevels("java_proto_common", JavaProtoSkylarkCommon.class); | ||
|
||
try { | ||
builder.addWorkspaceFilePrefix( | ||
ResourceFileLoader.loadResource(BazelJavaRuleClasses.class, "jdk.WORKSPACE")); | ||
} catch (IOException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public ImmutableList<RuleSet> requires() { | ||
return ImmutableList.of(CoreRules.INSTANCE, CcRules.INSTANCE); | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
src/main/java/com/google/devtools/build/lib/bazel/rules/ObjcRules.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Copyright 2018 The Bazel Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package com.google.devtools.build.lib.bazel.rules; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.Builder; | ||
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.RuleSet; | ||
import com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions; | ||
import com.google.devtools.build.lib.rules.apple.AppleConfiguration; | ||
import com.google.devtools.build.lib.rules.apple.AppleToolchain; | ||
import com.google.devtools.build.lib.rules.apple.XcodeConfigAlias.XcodeConfigAliasRule; | ||
import com.google.devtools.build.lib.rules.apple.XcodeConfigRule; | ||
import com.google.devtools.build.lib.rules.apple.XcodeVersionRule; | ||
import com.google.devtools.build.lib.rules.apple.cpp.AppleCcToolchainRule; | ||
import com.google.devtools.build.lib.rules.apple.swift.SwiftCommandLineOptions; | ||
import com.google.devtools.build.lib.rules.apple.swift.SwiftConfiguration; | ||
import com.google.devtools.build.lib.rules.core.CoreRules; | ||
import com.google.devtools.build.lib.rules.objc.AppleBinaryRule; | ||
import com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon; | ||
import com.google.devtools.build.lib.rules.objc.AppleStaticLibraryRule; | ||
import com.google.devtools.build.lib.rules.objc.AppleStubBinaryRule; | ||
import com.google.devtools.build.lib.rules.objc.IosDeviceRule; | ||
import com.google.devtools.build.lib.rules.objc.J2ObjcCommandLineOptions; | ||
import com.google.devtools.build.lib.rules.objc.J2ObjcConfiguration; | ||
import com.google.devtools.build.lib.rules.objc.ObjcBuildInfoFactory; | ||
import com.google.devtools.build.lib.rules.objc.ObjcBundleLibraryRule; | ||
import com.google.devtools.build.lib.rules.objc.ObjcBundleRule; | ||
import com.google.devtools.build.lib.rules.objc.ObjcCommandLineOptions; | ||
import com.google.devtools.build.lib.rules.objc.ObjcConfigurationLoader; | ||
import com.google.devtools.build.lib.rules.objc.ObjcFrameworkRule; | ||
import com.google.devtools.build.lib.rules.objc.ObjcImportRule; | ||
import com.google.devtools.build.lib.rules.objc.ObjcLibraryRule; | ||
import com.google.devtools.build.lib.rules.objc.ObjcProtoAspect; | ||
import com.google.devtools.build.lib.rules.objc.ObjcProtoLibraryRule; | ||
import com.google.devtools.build.lib.rules.objc.ObjcRuleClasses; | ||
|
||
/** | ||
* Rules for Objective-C support in Bazel. | ||
*/ | ||
public class ObjcRules implements RuleSet { | ||
public static final ObjcRules INSTANCE = new ObjcRules(); | ||
|
||
protected ObjcRules() { | ||
// Use the static INSTANCE field instead. | ||
} | ||
|
||
@Override | ||
public void init(Builder builder) { | ||
String toolsRepository = checkNotNull(builder.getToolsRepository()); | ||
|
||
// objc_proto_library should go into a separate RuleSet! | ||
// TODO(ulfjack): Depending on objcProtoAspect from here is a layering violation. | ||
ObjcProtoAspect objcProtoAspect = new ObjcProtoAspect(); | ||
|
||
builder.addBuildInfoFactory(new ObjcBuildInfoFactory()); | ||
builder.addSkylarkAccessibleTopLevels( | ||
"apple_common", new AppleSkylarkCommon(objcProtoAspect)); | ||
|
||
builder.addConfig(ObjcCommandLineOptions.class, new ObjcConfigurationLoader()); | ||
builder.addConfig(AppleCommandLineOptions.class, new AppleConfiguration.Loader()); | ||
builder.addConfig(SwiftCommandLineOptions.class, new SwiftConfiguration.Loader()); | ||
// j2objc shouldn't be here! | ||
builder.addConfig(J2ObjcCommandLineOptions.class, new J2ObjcConfiguration.Loader()); | ||
|
||
builder.addNativeAspectClass(objcProtoAspect); | ||
builder.addRuleDefinition(new AppleBinaryRule(objcProtoAspect)); | ||
builder.addRuleDefinition(new AppleStaticLibraryRule(objcProtoAspect)); | ||
builder.addRuleDefinition(new AppleStubBinaryRule()); | ||
builder.addRuleDefinition(new ObjcProtoLibraryRule(objcProtoAspect)); | ||
|
||
builder.addRuleDefinition(new AppleCcToolchainRule()); | ||
builder.addRuleDefinition(new AppleToolchain.RequiresXcodeConfigRule(toolsRepository)); | ||
builder.addRuleDefinition(new IosDeviceRule()); | ||
builder.addRuleDefinition(new ObjcBundleRule()); | ||
builder.addRuleDefinition(new ObjcBundleLibraryRule()); | ||
builder.addRuleDefinition(new ObjcFrameworkRule()); | ||
builder.addRuleDefinition(new ObjcImportRule()); | ||
builder.addRuleDefinition(new ObjcLibraryRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.CoptsRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.BundlingRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.DylibDependingRule(objcProtoAspect)); | ||
builder.addRuleDefinition(new ObjcRuleClasses.CompilingRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.LinkingRule(objcProtoAspect)); | ||
builder.addRuleDefinition(new ObjcRuleClasses.PlatformRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.MultiArchPlatformRule(objcProtoAspect)); | ||
builder.addRuleDefinition(new ObjcRuleClasses.ResourcesRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.AlwaysLinkRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.SdkFrameworksDependerRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.CompileDependencyRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.ResourceToolsRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.XcrunRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.LibtoolRule()); | ||
builder.addRuleDefinition(new ObjcRuleClasses.CrosstoolRule()); | ||
builder.addRuleDefinition(new XcodeConfigRule()); | ||
builder.addRuleDefinition(new XcodeConfigAliasRule()); | ||
builder.addRuleDefinition(new XcodeVersionRule()); | ||
} | ||
|
||
@Override | ||
public ImmutableList<RuleSet> requires() { | ||
return ImmutableList.of(CoreRules.INSTANCE, CcRules.INSTANCE); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.